From 1ca4e59f17ce19cbe1f82f9be28ffbb2c52cd8d5 Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 11:28:13 +0200 Subject: [PATCH 1/6] :art: move confirmationMessage logic to Confirmation handler. Remove nextMessageToUser from ConfirmedOnboardingResult --- src/xyz/didx/ai/AiHandler.scala | 10 +++++----- .../didx/ai/handler/ConfirmOnboardingResult.scala | 14 ++++++++++++++ src/xyz/didx/ai/model/ChatResult.scala | 1 - 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/xyz/didx/ai/AiHandler.scala b/src/xyz/didx/ai/AiHandler.scala index 1e71245..6e7d593 100644 --- a/src/xyz/didx/ai/AiHandler.scala +++ b/src/xyz/didx/ai/AiHandler.scala @@ -37,10 +37,7 @@ object AiHandler { // Results are records. Store in onboardingResults Map, and move to confirmation state onboardingResults.update(conversationId, result) - val response = "Thank you. Please confirm if the following recorded data is correct:\n\n" + - s"Name: ${result.fullName.getOrElse("None")}\n" + - s"Email: ${result.email.getOrElse("None")}\n" + - s"Cellphone: ${result.cellphone.getOrElse("None")}" + val response = ConfirmOnboardingHandler.getConfirmationMessage(result) (response, ChatState.ConfirmOnboardingResult) @@ -58,7 +55,10 @@ object AiHandler { case Some(onboardingResult) => val confirmationResult = ConfirmOnboardingHandler.getConfirmation(input, onboardingResult, conversationId) confirmationResult.confirmed match - case None => (confirmationResult.nextMessageToUser, state) + case None => ( + ConfirmOnboardingHandler.getReconfirmationMessage(onboardingResult), + ChatState.ConfirmOnboardingResult + ) case Some(true) => ( "Great, you are now ready to query the available Yoma opportunities! What would you like to do?", ChatState.QueryingOpportunities diff --git a/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala b/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala index 60939eb..22c10d7 100644 --- a/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala +++ b/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala @@ -40,4 +40,18 @@ object ConfirmOnboardingHandler { conversationId = Some(ConversationId(conversationId)) ) } + + def getConfirmationMessage(result: OnboardingResult): String = + "Thank you. Please confirm if the following recorded data is correct:\n\n" + + s"Name: ${result.fullName.getOrElse("None")}\n" + + s"Email: ${result.email.getOrElse("None")}\n" + + s"Cellphone: ${result.cellphone.getOrElse("None")}" + + def getReconfirmationMessage(result: OnboardingResult): String = + "Apologies, we couldn't infer if you confirmed or not. " + + "Please indicate \"yes\" or \"no\" if the following is correct:\n\n" + + s"Name: ${result.fullName.getOrElse("None")}\n" + + s"Email: ${result.email.getOrElse("None")}\n" + + s"Cellphone: ${result.cellphone.getOrElse("None")}" + } diff --git a/src/xyz/didx/ai/model/ChatResult.scala b/src/xyz/didx/ai/model/ChatResult.scala index 468a5b6..aafce95 100644 --- a/src/xyz/didx/ai/model/ChatResult.scala +++ b/src/xyz/didx/ai/model/ChatResult.scala @@ -12,7 +12,6 @@ case class OnboardingResult( Decoder case class ConfirmedOnboardingResult( - @Description("The next message that you want to send to the user") nextMessageToUser: String, @Description("User has confirmed that the recorded data is correct") confirmed: Option[Boolean] = None ) derives SerialDescriptor, Decoder From cea90415987db655b076bd492805793efe69af12 Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 11:29:00 +0200 Subject: [PATCH 2/6] :art: remove user onboard result from confirmation prompt builder --- src/xyz/didx/ai/AiHandler.scala | 2 +- .../ai/handler/ConfirmOnboardingResult.scala | 12 +++--------- src/xyz/didx/ai/model/AgentScript.scala | 17 ++++++----------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/xyz/didx/ai/AiHandler.scala b/src/xyz/didx/ai/AiHandler.scala index 6e7d593..f2a7475 100644 --- a/src/xyz/didx/ai/AiHandler.scala +++ b/src/xyz/didx/ai/AiHandler.scala @@ -53,7 +53,7 @@ object AiHandler { onboardingResultOpt match case None => ("Something went wrong retrieving recorded results. Let's try again!", ChatState.Onboarding) case Some(onboardingResult) => - val confirmationResult = ConfirmOnboardingHandler.getConfirmation(input, onboardingResult, conversationId) + val confirmationResult = ConfirmOnboardingHandler.getConfirmation(input, conversationId) confirmationResult.confirmed match case None => ( ConfirmOnboardingHandler.getReconfirmationMessage(onboardingResult), diff --git a/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala b/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala index 22c10d7..9b74e9d 100644 --- a/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala +++ b/src/xyz/didx/ai/handler/ConfirmOnboardingResult.scala @@ -10,22 +10,16 @@ import xyz.didx.ai.model.OnboardingResult import xyz.didx.ai.model.AgentScript object ConfirmOnboardingHandler { - // Define a map from conversationId to JvmPromptBuilder - private val builders: mutable.Map[String, PromptBuilder] = mutable.Map() - def getConfirmation( input: String, - onboardingResult: OnboardingResult, conversationId: String ): ConfirmedOnboardingResult = { scribe.info( s"Get ConfirmOnboardingResult response for message: $input, for conversationId: $conversationId" ) - // Get the builder for this conversationId, or create a new one if it doesn't exist - val builder = builders.getOrElseUpdate( - conversationId, - AgentScript.createConfirmationBuilder(onboardingResult) - ) + + // Get the prompt builder for this script + val builder = AgentScript.createConfirmationBuilder() // Add the user message to the builder builder.addUserMessage(input) diff --git a/src/xyz/didx/ai/model/AgentScript.scala b/src/xyz/didx/ai/model/AgentScript.scala index 010f442..4b7d137 100644 --- a/src/xyz/didx/ai/model/AgentScript.scala +++ b/src/xyz/didx/ai/model/AgentScript.scala @@ -25,18 +25,13 @@ object AgentScript { new JvmPromptBuilder().addSystemMessage(fullMessage) } - def createConfirmationBuilder(onboardingResult: OnboardingResult): PromptBuilder = { + def createConfirmationBuilder(): PromptBuilder = { val baseMessage = - "You are Yoma, an onboarding assistant! " + - "Your job is to receive confirmation from the user, whether the data we've recorded for that user is correct. " + - "We have the following data for them:\n" + - s"Name: ${onboardingResult.fullName.getOrElse("None")}\n" + - s"Email: ${onboardingResult.email.getOrElse("None")}\n" + - s"Cellphone: ${onboardingResult.cellphone.getOrElse("None")}\n" + - "Send this recorded data to the user, and ask them to please confirm if it's correct or not. " + - "They might respond with something like yes, indeed, correct, of course, :+1+, a thumbs up emoji, or other slang like shap - all of this would confirm their data (confirmed = True) " + - "If they respond with something like no, not correct, incorrect, of course not, :-1+, a thumbs down emoji, or other slang like wtf - all of this would confirm their data is incorrect (confirmed = False)" + - "If they give an unclear, blank, neutral or irrelevant response, we'll consider this to be confirmed = None, in which case you will kindly prompt them again!" + "Your job is to receive confirmation from the user - whether the data we've recorded is correct or not. " + + "They might respond with: yes, indeed, correct, of course, :+1+, a thumbs up emoji, slang like shap, or other general confirmation - all of this would confirm their data (confirmed = True). " + + "If they respond with: no, not correct, incorrect, of course not, :-1+, a thumbs down emoji, slang like wtf, or other general rejection - all of this would confirm their data is incorrect (confirmed = False). " + + "If they give an unclear, blank, neutral or irrelevant response, we'll consider this to be confirmed = None, in which case we will prompt them again. " + + "Please process their response to establish if confirmed = True, False, or None. " new JvmPromptBuilder().addSystemMessage(baseMessage) } From 2ee7717f8d1f76b0025816a6e83796faac1ad5c1 Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 11:31:15 +0200 Subject: [PATCH 3/6] :art: simplify onboarding script --- src/xyz/didx/ai/model/AgentScript.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/xyz/didx/ai/model/AgentScript.scala b/src/xyz/didx/ai/model/AgentScript.scala index 4b7d137..a3e1706 100644 --- a/src/xyz/didx/ai/model/AgentScript.scala +++ b/src/xyz/didx/ai/model/AgentScript.scala @@ -16,10 +16,9 @@ object AgentScript { "Name; Email; Cellphone. " } - val endMessage = "When receiving your first message from a user, begin asking for the info you still need. " + - "They can give one attribute at a time, or all at once. " + - "If a user has already given their name, email or cellphone in the chat, or if you already know it, then you shouldn't ask them again." + - "Be friendly." + val endMessage = "Prompt the user until you have the required info. " + + "If a user has already given their name, email or cellphone in the chat, then you shouldn't ask them again. " + + "Be friendly. When receiving a first message from a user, ask for the info you still need." val fullMessage = s"$baseMessage $conditionalMessage $endMessage" new JvmPromptBuilder().addSystemMessage(fullMessage) From dca19c2edab5ab1b4b1f3c0669661763a8f4e5f8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 14:32:20 +0200 Subject: [PATCH 4/6] :art: --- src/xyz/didx/ai/AiHandler.scala | 17 +++++++++++++---- src/xyz/didx/ai/model/AgentScript.scala | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/xyz/didx/ai/AiHandler.scala b/src/xyz/didx/ai/AiHandler.scala index f2a7475..60efa8d 100644 --- a/src/xyz/didx/ai/AiHandler.scala +++ b/src/xyz/didx/ai/AiHandler.scala @@ -32,16 +32,25 @@ object AiHandler { case ChatState.Onboarding => IO(Try { val result: OnboardingResult = OnboardingHandler.getResponse(input, conversationId, telNo) + scribe.info( + s"Got response from OnboardingHandler::getResponse, for conversationId: $conversationId" + ) val (messageToUser, nextState) = result match { case OnboardingResult(_, Some(fullName), Some(email), Some(cellphone)) => - // Results are records. Store in onboardingResults Map, and move to confirmation state - onboardingResults.update(conversationId, result) + scribe.info( + s"Recorded onboarding result for conversationId: $conversationId" + ) + + onboardingResults.update(conversationId, result) // Store in onboardingResults Map val response = ConfirmOnboardingHandler.getConfirmationMessage(result) - (response, ChatState.ConfirmOnboardingResult) + (response, ChatState.ConfirmOnboardingResult) // move to confirmation state - case OnboardingResult(_, _, _, _) => // In case data is not yet fully captured, remain in same state + case OnboardingResult(_, _, _, _) => + scribe.info( + s"Data is not yet fully captured, remain in same state for conversationId: $conversationId" + ) (result.nextMessageToUser, state) } (messageToUser, nextState) diff --git a/src/xyz/didx/ai/model/AgentScript.scala b/src/xyz/didx/ai/model/AgentScript.scala index a3e1706..209e8b1 100644 --- a/src/xyz/didx/ai/model/AgentScript.scala +++ b/src/xyz/didx/ai/model/AgentScript.scala @@ -29,8 +29,8 @@ object AgentScript { "Your job is to receive confirmation from the user - whether the data we've recorded is correct or not. " + "They might respond with: yes, indeed, correct, of course, :+1+, a thumbs up emoji, slang like shap, or other general confirmation - all of this would confirm their data (confirmed = True). " + "If they respond with: no, not correct, incorrect, of course not, :-1+, a thumbs down emoji, slang like wtf, or other general rejection - all of this would confirm their data is incorrect (confirmed = False). " + - "If they give an unclear, blank, neutral or irrelevant response, we'll consider this to be confirmed = None, in which case we will prompt them again. " + - "Please process their response to establish if confirmed = True, False, or None. " + "If they give an unclear, blank, neutral or irrelevant response, we'll consider this to be confirmed = None. " + + "Process their response and provide us with an Optional[Boolean]. confirmed = True, False, or None. " new JvmPromptBuilder().addSystemMessage(baseMessage) } From 9bb2c3512f8ec610a2e0ffb211f266c8366bf80f Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 14:32:35 +0200 Subject: [PATCH 5/6] test adjusting case class field name and description --- src/xyz/didx/ai/model/ChatResult.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xyz/didx/ai/model/ChatResult.scala b/src/xyz/didx/ai/model/ChatResult.scala index aafce95..1b1c2f1 100644 --- a/src/xyz/didx/ai/model/ChatResult.scala +++ b/src/xyz/didx/ai/model/ChatResult.scala @@ -12,6 +12,8 @@ case class OnboardingResult( Decoder case class ConfirmedOnboardingResult( - @Description("User has confirmed that the recorded data is correct") confirmed: Option[Boolean] = None + @Description("Optional[Boolean] whether user has confirmed or not") userHasConfirmedOptionalBool: Option[ + Boolean + ] = None ) derives SerialDescriptor, Decoder From 9aed4de2af3d9af163dce8eead798b19f07e5209 Mon Sep 17 00:00:00 2001 From: ff137 Date: Wed, 15 Nov 2023 15:22:31 +0200 Subject: [PATCH 6/6] update ConfirmedOnboardingResult to better ensure result serialises to Option[Boolean] --- src/xyz/didx/ai/AiHandler.scala | 2 +- src/xyz/didx/ai/model/ChatResult.scala | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/xyz/didx/ai/AiHandler.scala b/src/xyz/didx/ai/AiHandler.scala index 60efa8d..cfdc494 100644 --- a/src/xyz/didx/ai/AiHandler.scala +++ b/src/xyz/didx/ai/AiHandler.scala @@ -63,7 +63,7 @@ object AiHandler { case None => ("Something went wrong retrieving recorded results. Let's try again!", ChatState.Onboarding) case Some(onboardingResult) => val confirmationResult = ConfirmOnboardingHandler.getConfirmation(input, conversationId) - confirmationResult.confirmed match + confirmationResult.userHasConfirmedOptionalBool match case None => ( ConfirmOnboardingHandler.getReconfirmationMessage(onboardingResult), ChatState.ConfirmOnboardingResult diff --git a/src/xyz/didx/ai/model/ChatResult.scala b/src/xyz/didx/ai/model/ChatResult.scala index 1b1c2f1..a737b4a 100644 --- a/src/xyz/didx/ai/model/ChatResult.scala +++ b/src/xyz/didx/ai/model/ChatResult.scala @@ -12,8 +12,9 @@ case class OnboardingResult( Decoder case class ConfirmedOnboardingResult( - @Description("Optional[Boolean] whether user has confirmed or not") userHasConfirmedOptionalBool: Option[ - Boolean - ] = None + @Description("The next message that you want to send to the user") nextMessageToUser: String, + @Description( + "True, False, or None, to indicate whether user has confirmed or not" + ) userHasConfirmedOptionalBool: Option[Boolean] = None ) derives SerialDescriptor, Decoder