aws-lex-web-ui
aws-lex-web-ui copied to clipboard
Is it possible to auto trigger an intent on chat init
Any possibility to trigger an intent when chat get initiated or start. We have lex configuration initialText to display a start message. I want to show one intent with response cards on start of chat instead of "initialText". Something like initialIntent. I was trying this in Stand-Alone Page with Vue. Please let me know any such support or a workaround is available in lex web ui.
There is an API available for the LexWebUi which can be called once the component has loaded on the page. Within this API is a method named postText(). You could invoke this method after the LexWebUi has loaded to cause invocation of an initial Intent. See https://github.com/aws-samples/aws-lex-web-ui/blob/master/src/README.md#full-page-api---preview. You can also invoke postText via an API available on the Iframe based component loader. See https://github.com/aws-samples/aws-lex-web-ui/blob/master/src/README.md#iframe-api. postText() can be called via other elements on the page depending on your needs. For instance a series of buttons on the parent page or hosting page might call postText to invoke other intents.
@bobpskier Thank you for the solution. I have tried with vue component standalone version. Following a sample snippet of that.
// load the LexWebUi component
var lexWebUi = new LexWebUi.Loader(config);
// instantiate Vue
new Vue({
el: '#lex-web-ui',
store: lexWebUi.store,
template: '<div id="lex-web-ui-app"><lex-web-ui/></div>',
methods: {
postInitialLexMessage() {
var that = this;
this.$store.dispatch('lexPostText', 'hi').then(function(lex) {
that.$store.commit('pushMessage', lex)
});
// OR Below Bottom snippet dispatch too
}
},
mounted: function() {
this.postInitialLexMessage();
}
});
It's triggering an error "Uncaught (in promise) TypeError: Cannot read property 'getPromise' of undefined at Store.getCredentials (lex-web-ui.min.js:formatted:13411) ". This looks like postMessage is called before loader loaded got initated and crenditials get set. For a work around, I put timeout with zero milliseconds, it starts to work. Is there any good solution for this issue?
mounted: function() {
setTimeout(this.postInitialLexMessage, 0)
}
Below code works better than older version, but it too needs timeout.
this.$store.dispatch('postTextMessage', { type: '', text: 'Hi' });
Had it working with a chatbot ready handler
var initChatBot = () => {
// Fake a lex request to set welcome message
store.dispatch('lexPostText', 'welcome')
.then((response) => {
let alts = JSON.parse(response.sessionAttributes.appContext || '{}').altMessages;
store.dispatch(
'pushMessage',
{
text: response.message,
type: 'bot',
dialogState: store.state.lex.dialogState,
responseCard: store.state.lex.responseCard,
alts,
},
);
})
catch((error) => {
console.error(error);
});
}
But I also made other alterations to prevent initialText doing anything if it was an empty string.
add the listeners
// If in iFrame context, wait till fully ready
if (window.self !== window.top) {
window.addEventListener('message', onLexWebHandler, false);
} else {
document.addEventListener('fullpagecomponent', onLexWebHandler, false);
}
var onLexWebHandler = (evt) => {
let eventType = (evt.detail) ? evt.detail.event : ((evt.data) ? evt.data.event : "error");
if (eventType === "ready" || eventType === "parentReady") initChatBot()
...
}
In pure JS, you would do the following:
` function InitiateConversation() { UserInputText.value = 'Please wait...'; UserInputText.locked = true;
var params =
{
botAlias: BotAlias,
botName: BotName,
inputText: 'Welcome',
userId: lexUserId,
sessionAttributes: sessionAttributes
};
lexruntime.postText(params, function (err, data)
{
if (err)
{
console.log(err, err.stack);
showError('Error: ' + err.message + ' (see console for details)')
}
if (data)
{
// capture the sessionAttributes for the next cycle
sessionAttributes = data.sessionAttributes;
// show response and/or error/dialog status
showResponse(data);
}
// re-enable input
UserInputText.value = '';
UserInputText.locked = false;
}
);
}
`
I got the loader.api.postText('dummy');
to work. This invokes a response from the DummyIntent. But in the chat box it looks like a message "dummy" was sent by the user to lex. Is there a way to just hide that particular message box with the text "dummy" in it.
Thanks in advance.
With the code I posted above, the initial PostText isn't echoed to the screen. Check that you are doing similar.
Hey @VaderConsulting, Thank you for responding. I am using the "lex-web-ui-loader" for loading my Iframe, whereas the solution you have suggested uses "lexruntime". Can you help me implement this without me switching my architecture.
My current code snippet that I paste on my site looks like the following. When I plug in your function "InitiateConversation()" and make a call to it within my architecture I get the error that says "UserInputText" not defined. Any help here is much appretiated.
<script src="https://my_s3_bucket/lex-web-ui-loader.min.js"></script>
I am also trying to do this in an iframe loader. Would we put this in the parent page and call the iframe loader or put this in the index to load?
Hi @sumeet-kumar-deloitte
I'm also looking solution for similar issue. Please let me know if you are able to resolve it. Thanks in Advance
Regards, Maneesh
I'm sorry guys, I'm not even a front-end coder - I usually do backend stuff. The solution I posted works. If you want to do something different, then I don't have any experience to assist. I don't do Node and I really don't do Javascript.
Good luck
Dave
As of now, there is no direct way to do this. The only way is somehow we need to override the vuex actions initMessageList. That needs the core file changes, so out of scope as an actual solution. It'll better you use postText API with empty initialText config and a plain CSS to hide the first message from user.
Those who use Stand-Alone Page Directly Using the ChatBot UI Component Version can be able to override the actions initMessageList by defining before loader initialization.
LexWebUi.Store.actions.initMessageList = function(context) {
context.dispatch('lexPostText', '<YourIntentUtterance>').then(function(response) {
context.dispatch('pushMessage', {
text: response.message,
type: 'bot',
dialogState: response.dialogState,
responseCard: response.responseCard,
alts: {}
});
});
}
//Before loader get initialized
var lexWebUi = new LexWebUi.Loader(config);
NB: This is just a workaround, not a perfect solution.
Now here is my question: how can i hide the message bubble for initialUtterance only ('dummy' as per the example of this issue) from postText api request which is shown in iframe embedding: This is a part of code snippet which i am adding to my website html. I know there is a functionality of initialUtterance in the parameters but my case would be solved by achieving this thing only.
var loader = new ChatBotUiLoader.IframeLoader(loaderOpts); loader.load().then(function () { return loader.api.postText('Hi') }) .catch(function (error) { console.error(error); });
I want to pass the above code snippet (above mentioned code is just a part of snippet. I want to pass a complete one) according to if conditions For ex.: {if (locale===en){code snippet})}; something like this.
Can anyone please help me in modifying upper code snippet part in order to hide button message bubble for initial Utterance only.
Thanks in advance
@karanakatle I would experiment with setting the configuration value ui.hideButtonMessageBubble in lex-web-ui-loader-config.json to a value of true. This is the last element you would see by default in the ui section of the config file.
Display of input message bubbles in the Vue list are governed by whether they are being injected from a button click or via a human and the setting of this config value. Those that are from a button click can be optionally hidden via the setting above.
If ui.hidebuttonMessageBubble is true, any value in lex.initialUtterance will be injected such that it looks like it is coming from a button and will be hidden. This hides the initialUtterance from being displayed. This also prevents input text from other buttons in response cards from being displayed as a message bubble when they are clicked. We've found that if the initial utterance needs to be hidden, most users also prefer button text in general to be hidden from the message list when clicked. For example, in the Order Flowers sample bot, with this setting enabled, when the flower type is clicked on its value will not appear in the message list when sent to the bot.
@bobpskier I experimented that, but I want to hide only the initial utterance which we pass through api.postText, not the input Text from the buttons. Is there any way to achieve it. Could you please guide me in that case
@karanakatle I do not believe it is possible in the current implementation to achieve this affect. Probably the best approach to change this behavior is to leave the configuration ui.hideButtonMessageBubble set to the default of false but hardwire the sendInitialUtterance(context) to use a type of 'button'. Hence the initial utterance will have a type of 'button' and be hidden by default. At least that is my expectation. All other button clicks will still use a type of 'human' and be displayed. See the file lex-web-ui/src/store/actions.js. At line 80 of the current master branch of this file you'll find sendInitialUtterance(context){...}. If you change a couple of lines below, 'type:' to be a 'button' and instead of varying based on config, the initial utterance input should not be displayed. Build and deploy with this change I believe it will result in the effect you are looking for.
Hey thank you so much. I will work with it
@karanakatle Since this is still an open issue:
custom-chatbot-style.css
/* Hide the first human input message aka/dependent on initialUtterance */
.message-human:nth-of-type(1) {
display: none !important;
}
We had to init client.startNewSession with an arg for a messages object. (docs + docs)...
lex-web-ui/src/lib/lex/client.js
startNewSession(initialUtterance) {
let putSessionReq;
if (this.isV2Bot) {
putSessionReq = this.lexRuntimeClient.putSession({
botAliasId: this.botV2AliasId,
botId: this.botV2Id,
localeId: this.botV2LocaleId,
sessionId: this.userId,
sessionState: {
dialogAction: {
type: 'ElicitIntent',
},
},
/**
* SP-Edit
*
* If initialUtterance is set, pass its value to messages[0].content on start/restart.
* Otherwise, content.length must be > 0 on init when dialogaction.type is ElicitIntent
*
* @see {@link 'https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/lex-runtime-v2/command/PutSessionCommand/' PutSessionCommand}
*/
messages: [
{
content: initialUtterance || " ",
contentType: "PlainText",
},
],
/** /SP-Edit */
});
} else {
// ...
}
return
// ...
}
...to accommodate a bunch of side effects to a new-session Start-action,
lex-web-ui/src/store/actions.js
startNewSession(context) {
/** if first message is human (if initialUtterance has a value) otherwise " " */
const initialUtterance = context.state.config.lex.initialUtterance
? context.state.config.lex.initialUtterance
: " ";
context.commit('setIsLexProcessing', true);
return context.dispatch('refreshAuthTokens')
.then(() => context.dispatch('getCredentials'))
/**
* SP-Edit
* Always pass a string value to this
*/
.then(() => lexClient.startNewSession(initialUtterance))
/** /SP-Edit */
.then((data) => {
context.commit('setIsLexProcessing', false);
return context.dispatch('updateLexState', data)
.then(() => Promise.resolve(data));
})
.catch((error) => {
console.error(error);
context.commit('setIsLexProcessing', false);
});
},
via context.dispatch("startNewSession"); and so far the above has done the job of auto-triggering intent-on-init (and on every re init)