From 1ad810526285ba1111dd8a1135f1becc6a992478 Mon Sep 17 00:00:00 2001 From: Laurence Tews Date: Wed, 8 May 2019 18:37:58 +1200 Subject: [PATCH] Set re-prompts and hints into session during confused bot state (#15) * set reprompts and hints into session during confused bot state * Added test for confused knot with reprompts * undid explicit obj creation in logger --- .gitignore | 1 + gradle.properties | 2 +- .../mutters/core/util/SessionUtils.java | 2 +- .../rabidgremlin/mutters/bot/ink/InkBot.java | 66 +++++++++++-------- .../src/test/ink/taxi/confused.ink | 12 ++++ .../mutters/bot/ink/TestConfusedBot.java | 51 ++++++++++++-- .../src/test/resources/taxibot.ink.json | 2 +- 7 files changed, 101 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index ebf1a46..07c4cae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.gradle/ /build/ +.idea .project .classpath .settings diff --git a/gradle.properties b/gradle.properties index 0f9b7eb..dd05293 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.4.1 +version=4.4.2 # These are place holder values. Real values should be set in user's home gradle.properties # and are only needed when signing and uploading to central maven repo diff --git a/mutters-core/src/main/java/com/rabidgremlin/mutters/core/util/SessionUtils.java b/mutters-core/src/main/java/com/rabidgremlin/mutters/core/util/SessionUtils.java index 3b36234..bcb8cfb 100644 --- a/mutters-core/src/main/java/com/rabidgremlin/mutters/core/util/SessionUtils.java +++ b/mutters-core/src/main/java/com/rabidgremlin/mutters/core/util/SessionUtils.java @@ -123,7 +123,7 @@ public static void setNumberSlotIntoSession(Session session, String slotName, Nu * * @param session The session. * @param slotName The name of the slot. - * @param value The balue to store. + * @param value The value to store. */ public static void setStringSlotIntoSession(Session session, String slotName, String value) { diff --git a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java index 535bad1..ce295bf 100644 --- a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java +++ b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -67,7 +68,7 @@ public abstract class InkBot protected HashMap inkBotFunctions = new HashMap(); /** Map of global intents for the bot. */ - protected HashMap globalIntents = new HashMap(); + protected HashMap globalIntents = new HashMap<>(); /** Random for default reponses. */ private Random rand = new Random(); @@ -141,7 +142,7 @@ public InkBot(T configuration) List defaultResponses = configuration.getDefaultResponses(); if (defaultResponses != null) { - setDefaultResponses(defaultResponses.toArray(new String[defaultResponses.size()])); + setDefaultResponses(defaultResponses.toArray(new String[0])); } } @@ -156,7 +157,7 @@ public BotResponse respond(Session session, Context context, String messageText) throws BotException { log.debug("===> \n session: {} context: {} messageText: {}", - new Object[]{ session, context, messageText }); + new Object[]{ session, context, messageText }); CurrentResponse currentResponse = new CurrentResponse(); @@ -206,7 +207,7 @@ public BotResponse respond(Session session, Context context, String messageText) // restore the story state SessionUtils.loadInkStoryState(session, story.getState()); - // call hook so additional things can be applied to story after state has been restored + // call hook so additional things can be applied to story after state has been restored afterStoryStateLoaded(story); // get to right place in story, capture any pretext @@ -237,19 +238,7 @@ public BotResponse respond(Session session, Context context, String messageText) afterIntentMatch(intentMatch, session, story); // copy any slot values into ink vars - for (SlotMatch slotMatch : intentMatch.getSlotMatches().values()) - { - if (slotMatch.getValue() instanceof Number) - { - story.getVariablesState().set(slotMatch.getSlot().getName().toLowerCase(), - slotMatch.getValue()); - } - else - { - story.getVariablesState().set(slotMatch.getSlot().getName().toLowerCase(), - slotMatch.getValue().toString()); - } - } + setSlotValuesInInk(intentMatch.getSlotMatches().values(), story); // did we match something flag. Used so we can set reprompt correctly boolean foundMatch = false; @@ -294,17 +283,7 @@ public BotResponse respond(Session session, Context context, String messageText) // reset failed count failedToUnderstandCount = 0; - // set reprompt into session - if (currentResponse.getReprompt() != null) - { - SessionUtils.setReprompt(session, currentResponse.getReprompt()); - } - else - { - SessionUtils.setReprompt(session, defaultResponse + " " + currentResponse.getResponseText()); - } - SessionUtils.setRepromptHint(session, currentResponse.getHint()); - SessionUtils.setRepromptQuickReplies(session, currentResponse.getResponseQuickReplies()); + setRepromptInSession(currentResponse, session, defaultResponse); } else { @@ -331,6 +310,8 @@ public BotResponse respond(Session session, Context context, String messageText) getResponseText(session, currentResponse, story, intentMatch, false, preText); // reset failed count failedToUnderstandCount = 0; + + setRepromptInSession(currentResponse, session, defaultResponse); } // save failed count @@ -364,6 +345,35 @@ public BotResponse respond(Session session, Context context, String messageText) } } + private void setRepromptInSession(CurrentResponse currentResponse, Session session, String defaultResponse) + { + if (currentResponse.getReprompt() != null) + { + SessionUtils.setReprompt(session, currentResponse.getReprompt()); + } + else + { + SessionUtils.setReprompt(session, defaultResponse + " " + currentResponse.getResponseText()); + } + SessionUtils.setRepromptHint(session, currentResponse.getHint()); + SessionUtils.setRepromptQuickReplies(session, currentResponse.getResponseQuickReplies()); + } + + private void setSlotValuesInInk(Collection slotMatches, Story story) throws Exception + { + for (SlotMatch slotMatch : slotMatches) + { + if (slotMatch.getValue() instanceof Number) + { + story.getVariablesState().set(slotMatch.getSlot().getName().toLowerCase(), slotMatch.getValue()); + } + else + { + story.getVariablesState().set(slotMatch.getSlot().getName().toLowerCase(), slotMatch.getValue().toString()); + } + } + } + private void getResponseText(Session session, CurrentResponse currentResponse, Story story, IntentMatch intentMatch, boolean skipfirst, String preText) throws StoryException, Exception { diff --git a/mutters-ink-bot/src/test/ink/taxi/confused.ink b/mutters-ink-bot/src/test/ink/taxi/confused.ink index e89acaf..9fa3fce 100644 --- a/mutters-ink-bot/src/test/ink/taxi/confused.ink +++ b/mutters-ink-bot/src/test/ink/taxi/confused.ink @@ -2,3 +2,15 @@ I'm sorry I'm not understanding you at all :( If you are in a hurry, please call 555-12345 to order your taxi. -> END + +== confused_bot_with_handover == +I'm struggling with that one. Do you want me to call our service line for you? +::SET_REPROMPT Would you like me to call our service line? +::SET_HINT Yes or no +::ADD_QUICK_REPLY Yes +::ADD_QUICK_REPLY No ++ YesIntent + Calling our service operators now. Please hold the line. ++ NoIntent + Okay. I'm here if you need me. +-> END \ No newline at end of file diff --git a/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/TestConfusedBot.java b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/TestConfusedBot.java index 814a2cc..85d4f90 100644 --- a/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/TestConfusedBot.java +++ b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/TestConfusedBot.java @@ -2,24 +2,24 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertThat; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import com.rabidgremlin.mutters.bot.ink.InkBotConfiguration.ConfusedKnot; import com.rabidgremlin.mutters.core.Context; import com.rabidgremlin.mutters.core.bot.BotException; import com.rabidgremlin.mutters.core.bot.BotResponse; import com.rabidgremlin.mutters.core.session.Session; +import java.util.List; + public class TestConfusedBot { private TaxiInkBot botWithConfusedKnot; private TaxiInkBot botWithoutConfusedKnot; + private TaxiInkBot botWithConfusedKnotWithReprompts; class TaxiBotWithConfusedKnotConfig extends TaxiInkBotConfiguration @@ -30,6 +30,15 @@ public ConfusedKnot getConfusedKnot() return new ConfusedKnot(2, "confused_bot"); } } + + class TaxiBotWithConfusedKnotWithRepromptsConfig extends TaxiInkBotConfiguration + { + @Override + public ConfusedKnot getConfusedKnot() + { + return new ConfusedKnot(2, "confused_bot_with_handover"); + } + } class TaxiBotWithoutConfusedKnotConfig extends TaxiInkBotConfiguration { @@ -46,7 +55,8 @@ public ConfusedKnot getConfusedKnot() public void setUp() { botWithConfusedKnot = new TaxiInkBot(new TaxiBotWithConfusedKnotConfig()); - botWithoutConfusedKnot = new TaxiInkBot(new TaxiBotWithoutConfusedKnotConfig()); + botWithoutConfusedKnot = new TaxiInkBot(new TaxiBotWithoutConfusedKnotConfig()); + botWithConfusedKnotWithReprompts = new TaxiInkBot(new TaxiBotWithConfusedKnotWithRepromptsConfig()); } @@ -142,5 +152,38 @@ public void testStopConfusion() assertThat(response.isAskResponse(), is(false)); } + @Test + public void testConfusedKnotWithReprompts() + throws BotException + { + Session session = new Session(); + Context context = new Context(); + + BotResponse response = botWithConfusedKnotWithReprompts.respond(session, context, "Order me a taxi"); + + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("What is the pick up address ?")); + assertThat(response.isAskResponse(), is(true)); + + response = botWithConfusedKnotWithReprompts.respond(session, context, "skibidi whop"); + + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("Where would you like to be picked up ?")); + assertThat(response.isAskResponse(), is(true)); + + response = botWithConfusedKnotWithReprompts.respond(session, context, "Where is my cab ?"); + + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), startsWith("I'm struggling with that one. Do you want me to call our service line for you?")); + assertThat(response.isAskResponse(), is(true)); + assertThat(SessionUtils.getReprompt(session), is("Would you like me to call our service line?")); + + assertThat(response.getHint(), is("Yes or no")); + + assertThat(response.getQuickReplies().size(), is(2)); + List quickReplies = response.getQuickReplies(); + assertThat(quickReplies.get(0), is("Yes")); + assertThat(quickReplies.get(1), is("No")); + } } diff --git a/mutters-ink-bot/src/test/resources/taxibot.ink.json b/mutters-ink-bot/src/test/resources/taxibot.ink.json index 240bc02..dec5463 100644 --- a/mutters-ink-bot/src/test/resources/taxibot.ink.json +++ b/mutters-ink-bot/src/test/resources/taxibot.ink.json @@ -1 +1 @@ -{"inkVersion":17,"root":["\n","\n","\n","\n","\n","\n",{"->":"start"},"done",{"start":[["ev",{"^->":"start.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^OrderTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.0.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"order_taxi"},"\n",{"#f":7}]}],["ev",{"^->":"start.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CancelTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.1.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"cancel_taxi"},"\n",{"#f":7}]}],["ev",{"^->":"start.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^WhereTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.2.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"where_taxi"},"\n",{"#f":7}]}],{"#f":3}],"order_taxi":[[[["G>",["ev",{"VAR?":"address"},"str","^","/str","==","/ev",{"->":".^.b","c":true},{"b":[{"->":"order_taxi.request_address"},{"->":".^.^.^.3"},null]}],[{"->":".^.b"},{"b":[{"->":"order_taxi.order_the_taxi"},{"->":".^.^.^.3"},null]}],"nop","G<",null],"\n","end",{"#f":7,"#n":"order_taxi_loop"}],null],{"request_address":[["^What is the pick up address ?","\n","^::SET_REPROMPT Where would you like to be picked up ?","\n","^::SET_HINT 123 Someplace Rd","\n",["ev",{"^->":"order_taxi.request_address.0.6.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^GaveAddress",{"->":"$r","var":true},null],"c":["ev",{"^->":"order_taxi.request_address.0.6.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n",{"->":".^.^.^.g-0"},{"#f":7}]}],{"g-0":[{"->":".^.^.^.^.0.order_taxi_loop"},{"#f":7}]}],{"#f":3}],"order_the_taxi":["^::ORDER_TAXI","\n","^Taxi ",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ is on its way","\n","^::ADD_ATTACHMENT type::link url::http://trackcab.example.com/t/",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ title::Track your taxi here","\n","^::ADD_QUICK_REPLY Where is my taxi?","\n","^::ADD_QUICK_REPLY Cancel my taxi","\n","end",{"#f":3}],"#f":3}],"cancel_taxi":["^Your taxi has been cancelled","\n","end",{"#f":3}],"where_taxi":["^Your taxi is about 7 minutes away","\n","end",{"#f":3}],"stop":["^Ok","\n","end",{"#f":3}],"help":["^I can help you order a taxi or find out the location of your current taxi.","\n","^Try say \"Order a cab\" or \"Where is my cab\"","\n","end",{"#f":3}],"confused_bot":["^I'm sorry I'm not understanding you at all :(","\n","^If you are in a hurry, please call 555-12345 to order your taxi.","\n","end",{"#f":3}],"global decl":["ev","str","^","/str",{"VAR=":"address"},"str","^","/str",{"VAR=":"taxiNo"},"/ev","end",null],"#f":3}],"listDefs":{}} \ No newline at end of file +{"inkVersion":17,"root":["\n","\n","\n","\n","\n","\n",{"->":"start"},"done",{"start":[["ev",{"^->":"start.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^OrderTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.0.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"order_taxi"},"\n",null]}],["ev",{"^->":"start.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CancelTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.1.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"cancel_taxi"},"\n",null]}],["ev",{"^->":"start.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^WhereTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.2.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"where_taxi"},"\n",null]}],{"#f":3}],"order_taxi":[[[["G>",["ev",{"VAR?":"address"},"str","^","/str","==","/ev",{"->":".^.b","c":true},{"b":[{"->":"order_taxi.request_address"},{"->":".^.^.^.3"},null]}],[{"->":".^.b"},{"b":[{"->":"order_taxi.order_the_taxi"},{"->":".^.^.^.3"},null]}],"nop","G<",null],"\n","end",{"#n":"order_taxi_loop"}],null],{"request_address":[["^What is the pick up address ?","\n","^::SET_REPROMPT Where would you like to be picked up ?","\n","^::SET_HINT 123 Someplace Rd","\n",["ev",{"^->":"order_taxi.request_address.0.6.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^GaveAddress",{"->":"$r","var":true},null],"c":["ev",{"^->":"order_taxi.request_address.0.6.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n",{"->":".^.^.^.g-0"},null]}],{"g-0":[{"->":".^.^.^.^.0.order_taxi_loop"},null]}],{"#f":3}],"order_the_taxi":["^::ORDER_TAXI","\n","^Taxi ",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ is on its way","\n","^::ADD_ATTACHMENT type::link url::http://trackcab.example.com/t/",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ title::Track your taxi here","\n","^::ADD_QUICK_REPLY Where is my taxi?","\n","^::ADD_QUICK_REPLY Cancel my taxi","\n","end",{"#f":3}],"#f":3}],"cancel_taxi":["^Your taxi has been cancelled","\n","end",{"#f":3}],"where_taxi":["^Your taxi is about 7 minutes away","\n","end",{"#f":3}],"stop":["^Ok","\n","end",{"#f":3}],"help":["^I can help you order a taxi or find out the location of your current taxi.","\n","^Try say \"Order a cab\" or \"Where is my cab\"","\n","end",{"#f":3}],"confused_bot":["^I'm sorry I'm not understanding you at all :(","\n","^If you are in a hurry, please call 555-12345 to order your taxi.","\n","end",{"#f":3}],"confused_bot_with_handover":["^I'm struggling with that one. Do you want me to call our service line for you?","\n","^::SET_REPROMPT Would you like me to call our service line?","\n","^::SET_HINT Yes or no","\n","^::ADD_QUICK_REPLY Yes","\n","^::ADD_QUICK_REPLY No","\n",["ev",{"^->":"confused_bot_with_handover.10.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^YesIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"confused_bot_with_handover.10.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n","^Calling our service operators now. Please hold the line.","\n",null]}],["ev",{"^->":"confused_bot_with_handover.11.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^NoIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"confused_bot_with_handover.11.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n","^Okay. I'm here if you need me.","\n","end",null]}],{"#f":3}],"global decl":["ev","str","^","/str",{"VAR=":"address"},"str","^","/str",{"VAR=":"taxiNo"},"/ev","end",null],"#f":3}],"listDefs":{}} \ No newline at end of file