chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Step02.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the data structure for a form capturing details of a new customer, including personal information, contact details, account id and account type.<br/>
|
||||
/// Class used in <see cref="Step02a_AccountOpening"/>, <see cref="Step02b_AccountOpening"/> samples
|
||||
/// </summary>
|
||||
public class AccountDetails : NewCustomerForm
|
||||
{
|
||||
public Guid AccountId { get; set; }
|
||||
public AccountType AccountType { get; set; }
|
||||
}
|
||||
|
||||
public enum AccountType
|
||||
{
|
||||
PrimeABC,
|
||||
Other,
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace Step02.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Processes Events related to Account Opening scenarios.<br/>
|
||||
/// Class used in <see cref="Step02a_AccountOpening"/>, <see cref="Step02b_AccountOpening"/> samples
|
||||
/// </summary>
|
||||
public static class AccountOpeningEvents
|
||||
{
|
||||
public static readonly string StartProcess = nameof(StartProcess);
|
||||
|
||||
public static readonly string NewCustomerFormWelcomeMessageComplete = nameof(NewCustomerFormWelcomeMessageComplete);
|
||||
public static readonly string NewCustomerFormCompleted = nameof(NewCustomerFormCompleted);
|
||||
public static readonly string NewCustomerFormNeedsMoreDetails = nameof(NewCustomerFormNeedsMoreDetails);
|
||||
public static readonly string CustomerInteractionTranscriptReady = nameof(CustomerInteractionTranscriptReady);
|
||||
|
||||
public static readonly string NewAccountVerificationCheckPassed = nameof(NewAccountVerificationCheckPassed);
|
||||
|
||||
public static readonly string CreditScoreCheckApproved = nameof(CreditScoreCheckApproved);
|
||||
public static readonly string CreditScoreCheckRejected = nameof(CreditScoreCheckRejected);
|
||||
|
||||
public static readonly string FraudDetectionCheckPassed = nameof(FraudDetectionCheckPassed);
|
||||
public static readonly string FraudDetectionCheckFailed = nameof(FraudDetectionCheckFailed);
|
||||
|
||||
public static readonly string NewAccountDetailsReady = nameof(NewAccountDetailsReady);
|
||||
|
||||
public static readonly string NewMarketingRecordInfoReady = nameof(NewMarketingRecordInfoReady);
|
||||
public static readonly string NewMarketingEntryCreated = nameof(NewMarketingEntryCreated);
|
||||
public static readonly string CRMRecordInfoReady = nameof(CRMRecordInfoReady);
|
||||
public static readonly string CRMRecordInfoEntryCreated = nameof(CRMRecordInfoEntryCreated);
|
||||
|
||||
public static readonly string WelcomePacketCreated = nameof(WelcomePacketCreated);
|
||||
|
||||
public static readonly string MailServiceSent = nameof(MailServiceSent);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Step02.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the details of interactions between a user and service, including a unique identifier for the account,
|
||||
/// a transcript of conversation with the user, and the type of user interaction.<br/>
|
||||
/// Class used in <see cref="Step02a_AccountOpening"/>, <see cref="Step02b_AccountOpening"/> samples
|
||||
/// </summary>
|
||||
public record AccountUserInteractionDetails
|
||||
{
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
public List<ChatMessageContent> InteractionTranscript { get; set; } = [];
|
||||
|
||||
public UserInteractionType UserInteractionType { get; set; }
|
||||
}
|
||||
|
||||
public enum UserInteractionType
|
||||
{
|
||||
Complaint,
|
||||
AccountInfoRequest,
|
||||
OpeningNewAccount
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Step02.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Holds details for a new entry in a marketing database, including the account identifier, contact name, phone number, and email address.<br/>
|
||||
/// Class used in <see cref="Step02a_AccountOpening"/>, <see cref="Step02b_AccountOpening"/> samples
|
||||
/// </summary>
|
||||
public record MarketingNewEntryDetails
|
||||
{
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string PhoneNumber { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Step02.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the data structure for a form capturing details of a new customer, including personal information and contact details.<br/>
|
||||
/// Class used in <see cref="Step02a_AccountOpening"/>, <see cref="Step02b_AccountOpening"/> samples
|
||||
/// </summary>
|
||||
public class NewCustomerForm
|
||||
{
|
||||
[JsonPropertyName("userFirstName")]
|
||||
public string UserFirstName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userLastName")]
|
||||
public string UserLastName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userDateOfBirth")]
|
||||
public string UserDateOfBirth { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userState")]
|
||||
public string UserState { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userPhoneNumber")]
|
||||
public string UserPhoneNumber { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userId")]
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("userEmail")]
|
||||
public string UserEmail { get; set; } = string.Empty;
|
||||
|
||||
public NewCustomerForm CopyWithDefaultValues(string defaultStringValue = "Unanswered")
|
||||
{
|
||||
NewCustomerForm copy = new();
|
||||
PropertyInfo[] properties = typeof(NewCustomerForm).GetProperties();
|
||||
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
// Get the value of the property
|
||||
string? value = property.GetValue(this) as string;
|
||||
|
||||
// Check if the value is an empty string
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
property.SetValue(copy, defaultStringValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
property.SetValue(copy, value);
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public bool IsFormCompleted()
|
||||
{
|
||||
return !string.IsNullOrEmpty(UserFirstName) &&
|
||||
!string.IsNullOrEmpty(UserLastName) &&
|
||||
!string.IsNullOrEmpty(UserId) &&
|
||||
!string.IsNullOrEmpty(UserDateOfBirth) &&
|
||||
!string.IsNullOrEmpty(UserState) &&
|
||||
!string.IsNullOrEmpty(UserEmail) &&
|
||||
!string.IsNullOrEmpty(UserPhoneNumber);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
using Step02.Steps;
|
||||
|
||||
namespace Step02.Processes;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrate creation of <see cref="KernelProcess"/> and
|
||||
/// eliciting its response to five explicit user messages.<br/>
|
||||
/// For each test there is a different set of user messages that will cause different steps to be triggered using the same pipeline.<br/>
|
||||
/// For visual reference of the process check the <see href="https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/GettingStartedWithProcesses/README.md#step02b_accountOpening" >diagram</see>.
|
||||
/// </summary>
|
||||
public static class NewAccountCreationProcess
|
||||
{
|
||||
public static ProcessBuilder CreateProcess()
|
||||
{
|
||||
ProcessBuilder process = new("AccountCreationProcess");
|
||||
|
||||
var coreSystemRecordCreationStep = process.AddStepFromType<NewAccountStep>();
|
||||
var marketingRecordCreationStep = process.AddStepFromType<NewMarketingEntryStep>();
|
||||
var crmRecordStep = process.AddStepFromType<CRMRecordCreationStep>();
|
||||
var welcomePacketStep = process.AddStepFromType<WelcomePacketStep>();
|
||||
|
||||
// When the newCustomerForm is completed...
|
||||
process
|
||||
.OnInputEvent(AccountOpeningEvents.NewCustomerFormCompleted)
|
||||
// The information gets passed to the core system record creation step
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "customerDetails"));
|
||||
|
||||
// When the newCustomerForm is completed, the user interaction transcript with the user is passed to the core system record creation step
|
||||
process
|
||||
.OnInputEvent(AccountOpeningEvents.CustomerInteractionTranscriptReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "interactionTranscript"));
|
||||
|
||||
// When the fraudDetectionCheck step passes, the information gets to core system record creation step to kickstart this step
|
||||
process
|
||||
.OnInputEvent(AccountOpeningEvents.NewAccountVerificationCheckPassed)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "previousCheckSucceeded"));
|
||||
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will trigger the creation of a new marketing entry through the marketingRecordCreation step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewMarketingRecordInfoReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(marketingRecordCreationStep, functionName: NewMarketingEntryStep.ProcessStepFunctions.CreateNewMarketingEntry, parameterName: "userDetails"));
|
||||
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will trigger the creation of a new CRM entry through the crmRecord step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.CRMRecordInfoReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(crmRecordStep, functionName: CRMRecordCreationStep.ProcessStepFunctions.CreateCRMEntry, parameterName: "userInteractionDetails"));
|
||||
|
||||
// ParameterName is necessary when the step has multiple input arguments like welcomePacketStep.CreateWelcomePacketAsync
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will pass the account information details to the welcomePacket step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewAccountDetailsReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "accountDetails"));
|
||||
|
||||
// When the marketingRecordCreation step successfully creates a new marketing entry, it will notify the welcomePacket step it is ready
|
||||
marketingRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewMarketingEntryCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "marketingEntryCreated"));
|
||||
|
||||
// When the crmRecord step successfully creates a new CRM entry, it will notify the welcomePacket step it is ready
|
||||
crmRecordStep
|
||||
.OnEvent(AccountOpeningEvents.CRMRecordInfoEntryCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "crmRecordCreated"));
|
||||
|
||||
return process;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
using Step02.Steps;
|
||||
|
||||
namespace Step02.Processes;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrate creation of <see cref="KernelProcess"/> and
|
||||
/// eliciting its response to five explicit user messages.<br/>
|
||||
/// For each test there is a different set of user messages that will cause different steps to be triggered using the same pipeline.<br/>
|
||||
/// For visual reference of the process check the <see href="https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/GettingStartedWithProcesses/README.md#step02b_accountOpening" >diagram</see>.
|
||||
/// </summary>
|
||||
public static class NewAccountVerificationProcess
|
||||
{
|
||||
public static ProcessBuilder CreateProcess()
|
||||
{
|
||||
ProcessBuilder process = new("AccountVerificationProcess");
|
||||
|
||||
var customerCreditCheckStep = process.AddStepFromType<CreditScoreCheckStep>();
|
||||
var fraudDetectionCheckStep = process.AddStepFromType<FraudDetectionStep>();
|
||||
|
||||
// When the newCustomerForm is completed...
|
||||
process
|
||||
.OnInputEvent(AccountOpeningEvents.NewCustomerFormCompleted)
|
||||
// The information gets passed to the core system record creation step
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(customerCreditCheckStep, functionName: CreditScoreCheckStep.ProcessStepFunctions.DetermineCreditScore, parameterName: "customerDetails"))
|
||||
// The information gets passed to the fraud detection step for validation
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(fraudDetectionCheckStep, functionName: FraudDetectionStep.ProcessStepFunctions.FraudDetectionCheck, parameterName: "customerDetails"));
|
||||
|
||||
// When the creditScoreCheck step results in Approval, the information gets to the fraudDetection step to kickstart this step
|
||||
customerCreditCheckStep
|
||||
.OnEvent(AccountOpeningEvents.CreditScoreCheckApproved)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(fraudDetectionCheckStep, functionName: FraudDetectionStep.ProcessStepFunctions.FraudDetectionCheck, parameterName: "previousCheckSucceeded"));
|
||||
|
||||
return process;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Events;
|
||||
using Microsoft.SemanticKernel;
|
||||
using SharedSteps;
|
||||
using Step02.Models;
|
||||
using Step02.Steps;
|
||||
|
||||
namespace Step02;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrate creation of <see cref="KernelProcess"/> and
|
||||
/// eliciting its response to five explicit user messages.<br/>
|
||||
/// For each test there is a different set of user messages that will cause different steps to be triggered using the same pipeline.<br/>
|
||||
/// For visual reference of the process check the <see href="https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/GettingStartedWithProcesses/README.md#step02a_accountOpening" >diagram</see>.
|
||||
/// </summary>
|
||||
public class Step02a_AccountOpening(ITestOutputHelper output) : BaseTest(output, redirectSystemConsoleOutput: true)
|
||||
{
|
||||
// Target Open AI Services
|
||||
protected override bool ForceOpenAI => true;
|
||||
|
||||
private KernelProcess SetupAccountOpeningProcess<TUserInputStep>() where TUserInputStep : ScriptedUserInputStep
|
||||
{
|
||||
ProcessBuilder process = new("AccountOpeningProcess");
|
||||
var newCustomerFormStep = process.AddStepFromType<CompleteNewCustomerFormStep>();
|
||||
var userInputStep = process.AddStepFromType<TUserInputStep>();
|
||||
var displayAssistantMessageStep = process.AddStepFromType<DisplayAssistantMessageStep>();
|
||||
var customerCreditCheckStep = process.AddStepFromType<CreditScoreCheckStep>();
|
||||
var fraudDetectionCheckStep = process.AddStepFromType<FraudDetectionStep>();
|
||||
var mailServiceStep = process.AddStepFromType<MailServiceStep>();
|
||||
var coreSystemRecordCreationStep = process.AddStepFromType<NewAccountStep>();
|
||||
var marketingRecordCreationStep = process.AddStepFromType<NewMarketingEntryStep>();
|
||||
var crmRecordStep = process.AddStepFromType<CRMRecordCreationStep>();
|
||||
var welcomePacketStep = process.AddStepFromType<WelcomePacketStep>();
|
||||
|
||||
process.OnInputEvent(AccountOpeningEvents.StartProcess)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(newCustomerFormStep, CompleteNewCustomerFormStep.ProcessStepFunctions.NewAccountWelcome));
|
||||
|
||||
// When the welcome message is generated, send message to displayAssistantMessageStep
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormWelcomeMessageComplete)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(displayAssistantMessageStep, DisplayAssistantMessageStep.ProcessStepFunctions.DisplayAssistantMessage));
|
||||
|
||||
// When the userInput step emits a user input event, send it to the newCustomerForm step
|
||||
// Function names are necessary when the step has multiple public functions like CompleteNewCustomerFormStep: NewAccountWelcome and NewAccountProcessUserInfo
|
||||
userInputStep
|
||||
.OnEvent(CommonEvents.UserInputReceived)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(newCustomerFormStep, CompleteNewCustomerFormStep.ProcessStepFunctions.NewAccountProcessUserInfo, "userMessage"));
|
||||
|
||||
userInputStep
|
||||
.OnEvent(CommonEvents.Exit)
|
||||
.StopProcess();
|
||||
|
||||
// When the newCustomerForm step emits needs more details, send message to displayAssistantMessage step
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormNeedsMoreDetails)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(displayAssistantMessageStep, DisplayAssistantMessageStep.ProcessStepFunctions.DisplayAssistantMessage));
|
||||
|
||||
// After any assistant message is displayed, user input is expected to the next step is the userInputStep
|
||||
displayAssistantMessageStep
|
||||
.OnEvent(CommonEvents.AssistantResponseGenerated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(userInputStep, ScriptedUserInputStep.ProcessStepFunctions.GetUserInput));
|
||||
|
||||
// When the newCustomerForm is completed...
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormCompleted)
|
||||
// The information gets passed to the core system record creation step
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(customerCreditCheckStep, functionName: CreditScoreCheckStep.ProcessStepFunctions.DetermineCreditScore, parameterName: "customerDetails"))
|
||||
// The information gets passed to the fraud detection step for validation
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(fraudDetectionCheckStep, functionName: FraudDetectionStep.ProcessStepFunctions.FraudDetectionCheck, parameterName: "customerDetails"))
|
||||
// The information gets passed to the core system record creation step
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "customerDetails"));
|
||||
|
||||
// When the newCustomerForm is completed, the user interaction transcript with the user is passed to the core system record creation step
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.CustomerInteractionTranscriptReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "interactionTranscript"));
|
||||
|
||||
// When the creditScoreCheck step results in Rejection, the information gets to the mailService step to notify the user about the state of the application and the reasons
|
||||
customerCreditCheckStep
|
||||
.OnEvent(AccountOpeningEvents.CreditScoreCheckRejected)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep, functionName: MailServiceStep.ProcessStepFunctions.SendMailToUserWithDetails, parameterName: "message"));
|
||||
|
||||
// When the creditScoreCheck step results in Approval, the information gets to the fraudDetection step to kickstart this step
|
||||
customerCreditCheckStep
|
||||
.OnEvent(AccountOpeningEvents.CreditScoreCheckApproved)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(fraudDetectionCheckStep, functionName: FraudDetectionStep.ProcessStepFunctions.FraudDetectionCheck, parameterName: "previousCheckSucceeded"));
|
||||
|
||||
// When the fraudDetectionCheck step fails, the information gets to the mailService step to notify the user about the state of the application and the reasons
|
||||
fraudDetectionCheckStep
|
||||
.OnEvent(AccountOpeningEvents.FraudDetectionCheckFailed)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep, functionName: MailServiceStep.ProcessStepFunctions.SendMailToUserWithDetails, parameterName: "message"));
|
||||
|
||||
// When the fraudDetectionCheck step passes, the information gets to core system record creation step to kickstart this step
|
||||
fraudDetectionCheckStep
|
||||
.OnEvent(AccountOpeningEvents.FraudDetectionCheckPassed)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(coreSystemRecordCreationStep, functionName: NewAccountStep.ProcessStepFunctions.CreateNewAccount, parameterName: "previousCheckSucceeded"));
|
||||
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will trigger the creation of a new marketing entry through the marketingRecordCreation step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewMarketingRecordInfoReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(marketingRecordCreationStep, functionName: NewMarketingEntryStep.ProcessStepFunctions.CreateNewMarketingEntry, parameterName: "userDetails"));
|
||||
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will trigger the creation of a new CRM entry through the crmRecord step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.CRMRecordInfoReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(crmRecordStep, functionName: CRMRecordCreationStep.ProcessStepFunctions.CreateCRMEntry, parameterName: "userInteractionDetails"));
|
||||
|
||||
// ParameterName is necessary when the step has multiple input arguments like welcomePacketStep.CreateWelcomePacketAsync
|
||||
// When the coreSystemRecordCreation step successfully creates a new accountId, it will pass the account information details to the welcomePacket step
|
||||
coreSystemRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewAccountDetailsReady)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "accountDetails"));
|
||||
|
||||
// When the marketingRecordCreation step successfully creates a new marketing entry, it will notify the welcomePacket step it is ready
|
||||
marketingRecordCreationStep
|
||||
.OnEvent(AccountOpeningEvents.NewMarketingEntryCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "marketingEntryCreated"));
|
||||
|
||||
// When the crmRecord step successfully creates a new CRM entry, it will notify the welcomePacket step it is ready
|
||||
crmRecordStep
|
||||
.OnEvent(AccountOpeningEvents.CRMRecordInfoEntryCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(welcomePacketStep, parameterName: "crmRecordCreated"));
|
||||
|
||||
// After crmRecord and marketing gets created, a welcome packet is created to then send information to the user with the mailService step
|
||||
welcomePacketStep
|
||||
.OnEvent(AccountOpeningEvents.WelcomePacketCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep, functionName: MailServiceStep.ProcessStepFunctions.SendMailToUserWithDetails, parameterName: "message"));
|
||||
|
||||
// All possible paths end up with the user being notified about the account creation decision throw the mailServiceStep completion
|
||||
mailServiceStep
|
||||
.OnEvent(AccountOpeningEvents.MailServiceSent)
|
||||
.StopProcess();
|
||||
|
||||
KernelProcess kernelProcess = process.Build();
|
||||
|
||||
return kernelProcess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific userId and DOB that makes the creditScore and Fraud detection to pass
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessSuccessfulInteractionAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputSuccessfulInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific DOB that makes the creditScore to fail
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessFailureDueToCreditScoreFailureAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputCreditScoreFailureInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific userId that makes the fraudDetection to fail
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessFailureDueToFraudFailureAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputFraudFailureInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Events;
|
||||
using Microsoft.SemanticKernel;
|
||||
using SharedSteps;
|
||||
using Step02.Models;
|
||||
using Step02.Processes;
|
||||
using Step02.Steps;
|
||||
|
||||
namespace Step02;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrate creation of <see cref="KernelProcess"/> and
|
||||
/// eliciting its response to five explicit user messages.<br/>
|
||||
/// For each test there is a different set of user messages that will cause different steps to be triggered using the same pipeline.<br/>
|
||||
/// For visual reference of the process check the <see href="https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/GettingStartedWithProcesses/README.md#step02a_accountOpening" >diagram</see>.
|
||||
/// </summary>
|
||||
public class Step02b_AccountOpening(ITestOutputHelper output) : BaseTest(output, redirectSystemConsoleOutput: true)
|
||||
{
|
||||
// Target Open AI Services
|
||||
protected override bool ForceOpenAI => true;
|
||||
|
||||
private KernelProcess SetupAccountOpeningProcess<TUserInputStep>() where TUserInputStep : ScriptedUserInputStep
|
||||
{
|
||||
ProcessBuilder process = new("AccountOpeningProcessWithSubprocesses");
|
||||
var newCustomerFormStep = process.AddStepFromType<CompleteNewCustomerFormStep>();
|
||||
var userInputStep = process.AddStepFromType<TUserInputStep>();
|
||||
var displayAssistantMessageStep = process.AddStepFromType<DisplayAssistantMessageStep>();
|
||||
|
||||
var accountVerificationStep = process.AddStepFromProcess(NewAccountVerificationProcess.CreateProcess());
|
||||
var accountCreationStep = process.AddStepFromProcess(NewAccountCreationProcess.CreateProcess());
|
||||
|
||||
var mailServiceStep = process.AddStepFromType<MailServiceStep>();
|
||||
|
||||
process
|
||||
.OnInputEvent(AccountOpeningEvents.StartProcess)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(newCustomerFormStep, CompleteNewCustomerFormStep.ProcessStepFunctions.NewAccountWelcome));
|
||||
|
||||
// When the welcome message is generated, send message to displayAssistantMessageStep
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormWelcomeMessageComplete)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(displayAssistantMessageStep, DisplayAssistantMessageStep.ProcessStepFunctions.DisplayAssistantMessage));
|
||||
|
||||
// When the userInput step emits a user input event, send it to the newCustomerForm step
|
||||
// Function names are necessary when the step has multiple public functions like CompleteNewCustomerFormStep: NewAccountWelcome and NewAccountProcessUserInfo
|
||||
userInputStep
|
||||
.OnEvent(CommonEvents.UserInputReceived)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(newCustomerFormStep, CompleteNewCustomerFormStep.ProcessStepFunctions.NewAccountProcessUserInfo, "userMessage"));
|
||||
|
||||
userInputStep
|
||||
.OnEvent(CommonEvents.Exit)
|
||||
.StopProcess();
|
||||
|
||||
// When the newCustomerForm step emits needs more details, send message to displayAssistantMessage step
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormNeedsMoreDetails)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(displayAssistantMessageStep, DisplayAssistantMessageStep.ProcessStepFunctions.DisplayAssistantMessage));
|
||||
|
||||
// After any assistant message is displayed, user input is expected to the next step is the userInputStep
|
||||
displayAssistantMessageStep
|
||||
.OnEvent(CommonEvents.AssistantResponseGenerated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(userInputStep, ScriptedUserInputStep.ProcessStepFunctions.GetUserInput));
|
||||
|
||||
// When the newCustomerForm is completed...
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.NewCustomerFormCompleted)
|
||||
// The information gets passed to the account verificatino step
|
||||
.SendEventTo(accountVerificationStep.WhereInputEventIs(AccountOpeningEvents.NewCustomerFormCompleted))
|
||||
// The information gets passed to the validation process step
|
||||
.SendEventTo(accountCreationStep.WhereInputEventIs(AccountOpeningEvents.NewCustomerFormCompleted));
|
||||
|
||||
// When the newCustomerForm is completed, the user interaction transcript with the user is passed to the core system record creation step
|
||||
newCustomerFormStep
|
||||
.OnEvent(AccountOpeningEvents.CustomerInteractionTranscriptReady)
|
||||
.SendEventTo(accountCreationStep.WhereInputEventIs(AccountOpeningEvents.CustomerInteractionTranscriptReady));
|
||||
|
||||
// When the creditScoreCheck step results in Rejection, the information gets to the mailService step to notify the user about the state of the application and the reasons
|
||||
accountVerificationStep
|
||||
.OnEvent(AccountOpeningEvents.CreditScoreCheckRejected)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep));
|
||||
|
||||
// When the fraudDetectionCheck step fails, the information gets to the mailService step to notify the user about the state of the application and the reasons
|
||||
accountVerificationStep
|
||||
.OnEvent(AccountOpeningEvents.FraudDetectionCheckFailed)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep));
|
||||
|
||||
// When the fraudDetectionCheck step passes, the information gets to core system record creation step to kickstart this step
|
||||
accountVerificationStep
|
||||
.OnEvent(AccountOpeningEvents.FraudDetectionCheckPassed)
|
||||
.SendEventTo(accountCreationStep.WhereInputEventIs(AccountOpeningEvents.NewAccountVerificationCheckPassed));
|
||||
|
||||
// After crmRecord and marketing gets created, a welcome packet is created to then send information to the user with the mailService step
|
||||
accountCreationStep
|
||||
.OnEvent(AccountOpeningEvents.WelcomePacketCreated)
|
||||
.SendEventTo(new ProcessFunctionTargetBuilder(mailServiceStep));
|
||||
|
||||
// All possible paths end up with the user being notified about the account creation decision throw the mailServiceStep completion
|
||||
mailServiceStep
|
||||
.OnEvent(AccountOpeningEvents.MailServiceSent)
|
||||
.StopProcess();
|
||||
|
||||
KernelProcess kernelProcess = process.Build();
|
||||
|
||||
return kernelProcess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific userId and DOB that makes the creditScore and Fraud detection to pass
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessSuccessfulInteractionAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputSuccessfulInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific DOB that makes the creditScore to fail
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessFailureDueToCreditScoreFailureAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputCreditScoreFailureInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test uses a specific userId that makes the fraudDetection to fail
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UseAccountOpeningProcessFailureDueToFraudFailureAsync()
|
||||
{
|
||||
Kernel kernel = CreateKernelWithChatCompletion();
|
||||
KernelProcess kernelProcess = SetupAccountOpeningProcess<UserInputFraudFailureInteractionStep>();
|
||||
await using var runningProcess = await kernelProcess.StartAsync(kernel, new KernelProcessEvent() { Id = AccountOpeningEvents.StartProcess, Data = null });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates the creation of a new CRM entry
|
||||
/// </summary>
|
||||
public class CRMRecordCreationStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string CreateCRMEntry = nameof(CreateCRMEntry);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.CreateCRMEntry)]
|
||||
public async Task CreateCRMEntryAsync(KernelProcessStepContext context, AccountUserInteractionDetails userInteractionDetails, Kernel _kernel)
|
||||
{
|
||||
Console.WriteLine($"[CRM ENTRY CREATION] New Account {userInteractionDetails.AccountId} created");
|
||||
|
||||
// Placeholder for a call to API to create new CRM entry
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.CRMRecordInfoEntryCreated, Data = true });
|
||||
}
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step that is helps the user fill up a new account form.<br/>
|
||||
/// Also provides a welcome message for the user.
|
||||
/// </summary>
|
||||
public class CompleteNewCustomerFormStep : KernelProcessStep<NewCustomerFormState>
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string NewAccountProcessUserInfo = nameof(NewAccountProcessUserInfo);
|
||||
public const string NewAccountWelcome = nameof(NewAccountWelcome);
|
||||
}
|
||||
|
||||
internal NewCustomerFormState? _state;
|
||||
|
||||
internal string _formCompletionSystemPrompt = """
|
||||
The goal is to fill up all the fields needed for a form.
|
||||
The user may provide information to fill up multiple fields of the form in one message.
|
||||
The user needs to fill up a form, all the fields of the form are necessary
|
||||
|
||||
<CURRENT_FORM_STATE>
|
||||
{{current_form_state}}
|
||||
<CURRENT_FORM_STATE>
|
||||
|
||||
GUIDANCE:
|
||||
- If there are missing details, give the user a useful message that will help fill up the remaining fields.
|
||||
- Your goal is to help guide the user to provide the missing details on the current form.
|
||||
- Encourage the user to provide the remainingdetails with examples if necessary.
|
||||
- Fields with value 'Unanswered' need to be answered by the user.
|
||||
- Format phone numbers and user ids correctly if the user does not provide the expected format.
|
||||
- If the user does not make use of parenthesis in the phone number, add them.
|
||||
- For date fields, confirm with the user first if the date format is not clear. Example 02/03 03/02 could be March 2nd or February 3rd.
|
||||
""";
|
||||
|
||||
internal string _welcomeMessage = """
|
||||
Hello there, I can help you out fill out the information needed to open a new account with us.
|
||||
Please provide some personal information like first name and last name to get started.
|
||||
""";
|
||||
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never
|
||||
};
|
||||
|
||||
public override ValueTask ActivateAsync(KernelProcessStepState<NewCustomerFormState> state)
|
||||
{
|
||||
_state = state.State;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.NewAccountWelcome)]
|
||||
public async Task NewAccountWelcomeMessageAsync(KernelProcessStepContext context, Kernel _kernel)
|
||||
{
|
||||
_state?.conversation.Add(new ChatMessageContent { Role = AuthorRole.Assistant, Content = _welcomeMessage });
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.NewCustomerFormWelcomeMessageComplete, Data = _welcomeMessage });
|
||||
}
|
||||
|
||||
private Kernel CreateNewCustomerFormKernel(Kernel _baseKernel)
|
||||
{
|
||||
// Creating another kernel that only makes use private functions to fill up the new customer form
|
||||
Kernel kernel = new(_baseKernel.Services);
|
||||
kernel.ImportPluginFromFunctions("FillForm", [
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedFirstName, functionName: nameof(OnUserProvidedFirstName)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedLastName, functionName: nameof(OnUserProvidedLastName)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedDOBDetails, functionName: nameof(OnUserProvidedDOBDetails)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedStateOfResidence, functionName: nameof(OnUserProvidedStateOfResidence)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedPhoneNumber, functionName: nameof(OnUserProvidedPhoneNumber)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedUserId, functionName: nameof(OnUserProvidedUserId)),
|
||||
KernelFunctionFactory.CreateFromMethod(OnUserProvidedEmailAddress, functionName: nameof(OnUserProvidedEmailAddress)),
|
||||
]);
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.NewAccountProcessUserInfo)]
|
||||
public async Task CompleteNewCustomerFormAsync(KernelProcessStepContext context, string userMessage, Kernel _kernel)
|
||||
{
|
||||
// Keeping track of all user interactions
|
||||
_state?.conversation.Add(new ChatMessageContent { Role = AuthorRole.User, Content = userMessage });
|
||||
|
||||
Kernel kernel = CreateNewCustomerFormKernel(_kernel);
|
||||
|
||||
OpenAIPromptExecutionSettings settings = new()
|
||||
{
|
||||
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
|
||||
Temperature = 0.7,
|
||||
MaxTokens = 2048
|
||||
};
|
||||
|
||||
ChatHistory chatHistory = [];
|
||||
chatHistory.AddSystemMessage(_formCompletionSystemPrompt
|
||||
.Replace("{{current_form_state}}", JsonSerializer.Serialize(_state!.newCustomerForm.CopyWithDefaultValues(), _jsonOptions)));
|
||||
chatHistory.AddRange(_state.conversation);
|
||||
IChatCompletionService chatService = kernel.Services.GetRequiredService<IChatCompletionService>();
|
||||
ChatMessageContent response = await chatService.GetChatMessageContentAsync(chatHistory, settings, kernel).ConfigureAwait(false);
|
||||
var assistantResponse = "";
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
assistantResponse = response.Items[0].ToString();
|
||||
// Keeping track of all assistant interactions
|
||||
_state?.conversation.Add(new ChatMessageContent { Role = AuthorRole.Assistant, Content = assistantResponse });
|
||||
}
|
||||
|
||||
if (_state?.newCustomerForm != null && _state.newCustomerForm.IsFormCompleted())
|
||||
{
|
||||
Console.WriteLine($"[NEW_USER_FORM_COMPLETED]: {JsonSerializer.Serialize(_state?.newCustomerForm)}");
|
||||
// All user information is gathered to proceed to the next step
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.NewCustomerFormCompleted, Data = _state?.newCustomerForm, Visibility = KernelProcessEventVisibility.Public });
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.CustomerInteractionTranscriptReady, Data = _state?.conversation, Visibility = KernelProcessEventVisibility.Public });
|
||||
return;
|
||||
}
|
||||
|
||||
// emit event: NewCustomerFormNeedsMoreDetails
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.NewCustomerFormNeedsMoreDetails, Data = assistantResponse });
|
||||
}
|
||||
|
||||
[Description("User provided details of first name")]
|
||||
private Task OnUserProvidedFirstName(string firstName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(firstName) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserFirstName = firstName;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided details of last name")]
|
||||
private Task OnUserProvidedLastName(string lastName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(lastName) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserLastName = lastName;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided details of USA State the user lives in, must be in 2-letter Uppercase State Abbreviation format")]
|
||||
private Task OnUserProvidedStateOfResidence(string stateAbbreviation)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stateAbbreviation) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserState = stateAbbreviation;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided details of date of birth, must be in the format MM/DD/YYYY")]
|
||||
private Task OnUserProvidedDOBDetails(string date)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(date) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserDateOfBirth = date;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided details of phone number, must be in the format (\\d{3})-\\d{3}-\\d{4}")]
|
||||
private Task OnUserProvidedPhoneNumber(string phoneNumber)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(phoneNumber) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserPhoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided details of userId, must be in the format \\d{3}-\\d{3}-\\d{4}")]
|
||||
private Task OnUserProvidedUserId(string userId)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(userId) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserId = userId;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Description("User provided email address, must be in the an email valid format")]
|
||||
private Task OnUserProvidedEmailAddress(string emailAddress)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(emailAddress) && _state != null)
|
||||
{
|
||||
_state.newCustomerForm.UserEmail = emailAddress;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state object for the <see cref="CompleteNewCustomerFormStep"/>
|
||||
/// </summary>
|
||||
public class NewCustomerFormState
|
||||
{
|
||||
internal NewCustomerForm newCustomerForm { get; set; } = new();
|
||||
internal List<ChatMessageContent> conversation { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates User Credit Score check, based on the date of birth the score will be enough or insufficient
|
||||
/// </summary>
|
||||
public class CreditScoreCheckStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string DetermineCreditScore = nameof(DetermineCreditScore);
|
||||
}
|
||||
|
||||
private const int MinCreditScore = 600;
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.DetermineCreditScore)]
|
||||
public async Task DetermineCreditScoreAsync(KernelProcessStepContext context, NewCustomerForm customerDetails, Kernel _kernel)
|
||||
{
|
||||
// Placeholder for a call to API to validate credit score with customerDetails
|
||||
var creditScore = customerDetails.UserDateOfBirth == "02/03/1990" ? 700 : 500;
|
||||
|
||||
if (creditScore >= MinCreditScore)
|
||||
{
|
||||
Console.WriteLine("[CREDIT CHECK] Credit Score Check Passed");
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.CreditScoreCheckApproved, Data = true });
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("[CREDIT CHECK] Credit Score Check Failed");
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.CreditScoreCheckRejected,
|
||||
Data = $"We regret to inform you that your credit score of {creditScore} is insufficient to apply for an account of the type PRIME ABC",
|
||||
Visibility = KernelProcessEventVisibility.Public,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates a Fraud detection check, based on the userId the fraud detection will pass or fail.
|
||||
/// </summary>
|
||||
public class FraudDetectionStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string FraudDetectionCheck = nameof(FraudDetectionCheck);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.FraudDetectionCheck)]
|
||||
public async Task FraudDetectionCheckAsync(KernelProcessStepContext context, bool previousCheckSucceeded, NewCustomerForm customerDetails, Kernel _kernel)
|
||||
{
|
||||
// Placeholder for a call to API to validate user details for fraud detection
|
||||
if (customerDetails.UserId == "123-456-7890")
|
||||
{
|
||||
Console.WriteLine("[FRAUD CHECK] Fraud Check Failed");
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.FraudDetectionCheckFailed,
|
||||
Data = "We regret to inform you that we found some inconsistent details regarding the information you provided regarding the new account of the type PRIME ABC you applied.",
|
||||
Visibility = KernelProcessEventVisibility.Public,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("[FRAUD CHECK] Fraud Check Passed");
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.FraudDetectionCheckPassed, Data = true, Visibility = KernelProcessEventVisibility.Public });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates Mail Service with a message for the user.
|
||||
/// </summary>
|
||||
public class MailServiceStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string SendMailToUserWithDetails = nameof(SendMailToUserWithDetails);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.SendMailToUserWithDetails)]
|
||||
public async Task SendMailServiceAsync(KernelProcessStepContext context, string message)
|
||||
{
|
||||
Console.WriteLine("======== MAIL SERVICE ======== ");
|
||||
Console.WriteLine(message);
|
||||
Console.WriteLine("============================== ");
|
||||
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.MailServiceSent, Data = message });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates the creation of a new account that triggers other services after a new account id creation
|
||||
/// </summary>
|
||||
public class NewAccountStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string CreateNewAccount = nameof(CreateNewAccount);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.CreateNewAccount)]
|
||||
public async Task CreateNewAccountAsync(KernelProcessStepContext context, bool previousCheckSucceeded, NewCustomerForm customerDetails, List<ChatMessageContent> interactionTranscript, Kernel _kernel)
|
||||
{
|
||||
// Placeholder for a call to API to create new account for user
|
||||
var accountId = new Guid();
|
||||
AccountDetails accountDetails = new()
|
||||
{
|
||||
UserDateOfBirth = customerDetails.UserDateOfBirth,
|
||||
UserFirstName = customerDetails.UserFirstName,
|
||||
UserLastName = customerDetails.UserLastName,
|
||||
UserId = customerDetails.UserId,
|
||||
UserPhoneNumber = customerDetails.UserPhoneNumber,
|
||||
UserState = customerDetails.UserState,
|
||||
UserEmail = customerDetails.UserEmail,
|
||||
AccountId = accountId,
|
||||
AccountType = AccountType.PrimeABC,
|
||||
};
|
||||
|
||||
Console.WriteLine($"[ACCOUNT CREATION] New Account {accountId} created");
|
||||
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.NewMarketingRecordInfoReady,
|
||||
Data = new MarketingNewEntryDetails
|
||||
{
|
||||
AccountId = accountId,
|
||||
Name = $"{customerDetails.UserFirstName} {customerDetails.UserLastName}",
|
||||
PhoneNumber = customerDetails.UserPhoneNumber,
|
||||
Email = customerDetails.UserEmail,
|
||||
}
|
||||
});
|
||||
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.CRMRecordInfoReady,
|
||||
Data = new AccountUserInteractionDetails
|
||||
{
|
||||
AccountId = accountId,
|
||||
UserInteractionType = UserInteractionType.OpeningNewAccount,
|
||||
InteractionTranscript = interactionTranscript
|
||||
}
|
||||
});
|
||||
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.NewAccountDetailsReady,
|
||||
Data = accountDetails,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates the creation a new marketing user entry.
|
||||
/// </summary>
|
||||
public class NewMarketingEntryStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string CreateNewMarketingEntry = nameof(CreateNewMarketingEntry);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.CreateNewMarketingEntry)]
|
||||
public async Task CreateNewMarketingEntryAsync(KernelProcessStepContext context, MarketingNewEntryDetails userDetails, Kernel _kernel)
|
||||
{
|
||||
Console.WriteLine($"[MARKETING ENTRY CREATION] New Account {userDetails.AccountId} created");
|
||||
|
||||
// Placeholder for a call to API to create new entry of user for marketing purposes
|
||||
await context.EmitEventAsync(new() { Id = AccountOpeningEvents.NewMarketingEntryCreated, Data = true });
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using SharedSteps;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ScriptedUserInputStep"/> Step with interactions that makes the Process fail due credit score failure
|
||||
/// </summary>
|
||||
public sealed class UserInputCreditScoreFailureInteractionStep : ScriptedUserInputStep
|
||||
{
|
||||
public override void PopulateUserInputs(UserInputState state)
|
||||
{
|
||||
state.UserInputs.Add("I would like to open an account");
|
||||
state.UserInputs.Add("My name is John Contoso, dob 01/01/1990");
|
||||
state.UserInputs.Add("I live in Washington and my phone number es 222-222-1234");
|
||||
state.UserInputs.Add("My userId is 987-654-3210");
|
||||
state.UserInputs.Add("My email is john.contoso@contoso.com, what else do you need?");
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using SharedSteps;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ScriptedUserInputStep"/> Step with interactions that makes the Process fail due fraud detection failure
|
||||
/// </summary>
|
||||
public sealed class UserInputFraudFailureInteractionStep : ScriptedUserInputStep
|
||||
{
|
||||
public override void PopulateUserInputs(UserInputState state)
|
||||
{
|
||||
state.UserInputs.Add("I would like to open an account");
|
||||
state.UserInputs.Add("My name is John Contoso, dob 02/03/1990");
|
||||
state.UserInputs.Add("I live in Washington and my phone number es 222-222-1234");
|
||||
state.UserInputs.Add("My userId is 123-456-7890");
|
||||
state.UserInputs.Add("My email is john.contoso@contoso.com, what else do you need?");
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using SharedSteps;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ScriptedUserInputStep"/> Step with interactions that makes the Process pass all steps and successfully open a new account
|
||||
/// </summary>
|
||||
public sealed class UserInputSuccessfulInteractionStep : ScriptedUserInputStep
|
||||
{
|
||||
public override void PopulateUserInputs(UserInputState state)
|
||||
{
|
||||
state.UserInputs.Add("I would like to open an account");
|
||||
state.UserInputs.Add("My name is John Contoso, dob 02/03/1990");
|
||||
state.UserInputs.Add("I live in Washington and my phone number es 222-222-1234");
|
||||
state.UserInputs.Add("My userId is 987-654-3210");
|
||||
state.UserInputs.Add("My email is john.contoso@contoso.com, what else do you need?");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Step02.Models;
|
||||
|
||||
namespace Step02.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Mock step that emulates the creation of a Welcome Packet for a new user after account creation
|
||||
/// </summary>
|
||||
public class WelcomePacketStep : KernelProcessStep
|
||||
{
|
||||
public static class ProcessStepFunctions
|
||||
{
|
||||
public const string CreateWelcomePacket = nameof(CreateWelcomePacket);
|
||||
}
|
||||
|
||||
[KernelFunction(ProcessStepFunctions.CreateWelcomePacket)]
|
||||
public async Task CreateWelcomePacketAsync(KernelProcessStepContext context, bool marketingEntryCreated, bool crmRecordCreated, AccountDetails accountDetails, Kernel _kernel)
|
||||
{
|
||||
Console.WriteLine($"[WELCOME PACKET] New Account {accountDetails.AccountId} created");
|
||||
|
||||
var mailMessage = $"""
|
||||
Dear {accountDetails.UserFirstName} {accountDetails.UserLastName}
|
||||
We are thrilled to inform you that you have successfully created a new PRIME ABC Account with us!
|
||||
|
||||
Account Details:
|
||||
Account Number: {accountDetails.AccountId}
|
||||
Account Type: {accountDetails.AccountType}
|
||||
|
||||
Please keep this confidential for security purposes.
|
||||
|
||||
Here is the contact information we have in file:
|
||||
|
||||
Email: {accountDetails.UserEmail}
|
||||
Phone: {accountDetails.UserPhoneNumber}
|
||||
|
||||
Thank you for opening an account with us!
|
||||
""";
|
||||
|
||||
await context.EmitEventAsync(new()
|
||||
{
|
||||
Id = AccountOpeningEvents.WelcomePacketCreated,
|
||||
Data = mailMessage,
|
||||
Visibility = KernelProcessEventVisibility.Public,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user