Skip to content

Instantly share code, notes, and snippets.

@israelias
Last active September 4, 2024 10:55
Show Gist options
  • Save israelias/9f7cbdb9017fd179c6110216f3daf913 to your computer and use it in GitHub Desktop.
Save israelias/9f7cbdb9017fd179c6110216f3daf913 to your computer and use it in GitHub Desktop.
lex-web-ui ultimate refresh
/**
* lex-web-ui/src/store/actions.js
* Ultimate restart action (automated when user arrives at "start module"
* typically on `QID: Startmodule` or utterance "restart/start over" or `QID: Restart`
*
* we add a message.label and show only message.label to circumvent showing `QID: <module>` to user
*/
import LexClient from '@/lib/lex/client';
// non-state variables that may be mutated outside of store
// set via initializers at run time
// ...
let lexClient;
// ...
export default {
/** ... */
/**
* SP-Edit
* sends the initial utterance configured in the Lex config
* to start the conversation.
*
* Originally:
* It dispatches the postTextMessage action to send the utterance
*
* Modified:
* If the initialUtteranceSent flag is false in state, it sends the configured
* initialUtterance text.
*
* If initialUtteranceSent is true, it sends a "Restart" message to restart the
* conversation.
*/
sendInitialUtterance(context) {
if (context.state.config.lex.initialUtterance) {
let message = {
type: context.state.config.ui.hideButtonMessageBubble ? 'button' : 'human',
text: context.state.config.lex.initialUtterance,
label: 'Restart',
};
if (!context.state.initialUtteranceSent) {
console.info('Sending initial utterance as configured');
message.text = context.state.config.lex.initialUtterance;
} else {
console.info('Sending initial utterance as Restart');
message.label = 'Restart';
}
context.dispatch('postTextMessage', message);
}
},
/** /SP-Edit */
/** ... */
// see SP-Addition in this long method
postTextMessage(context, message) {
if (context.state.isSFXOn && !context.state.lex.isPostTextRetry) {
context.dispatch('playSound', context.state.config.ui.messageSentSFX);
}
return context.dispatch('interruptSpeechConversation')
.then(() => {
/**
* SP-Addition
* If messsage.text === initialUtterance or "Restart",
* Start a new session, which issues a new sessionId.
*/
const initialUtterance = context.state.config.lex.initialUtterance
? context.state.config.lex.initialUtterance
: "";
const qidInitialUtterance = `QID::${initialUtterance}`;
if (
initialUtterance.length > 0 &&
(message.text === initialUtterance ||
message.text === qidInitialUtterance ||
message.label === "Restart"
)
) {
console.info('postTextMessage: triggered initialUtterance', message.text);
if (lexClient) {
lexClient.createSessionId();
console.info('A new session id has been issued');
console.info('Clearing messages', context.state.messages);
context.commit('clearMessages');
console.info('Messages cleared', context.state.messages);
console.info('Clearing utterance', context.state.utteranceStack);
context.commit('clearUtteranceStack');
console.info('Utterances cleared', context.state.utteranceStack);
return Promise.resolve();
}
}
/** /SP-Addition */
return Promise.resolve();
})
/** ... */
/** SKIP REST OF THIS METHOD HAS NOT BEEN MODIFIED */
/** ... */
.then(() => {
/** ... */
})
.then(() => {
/** ... */
})
.then(() => {
/** ... */
})
.then((response) => {
/** ... */
.then(() => {
/** ... */
})
.catch((error) => {
/** ... */
});
},
/** ... */
/**
* SP-Edit
* the action the resets the chat window when saveHistory: true
* Adds commit to clear the utterance stack along with the messages
* adds dispatch to send initial utterance
* along with initial text with bot if it's set
* @todo unused and consider adding logic between initialText and initialUtterance
*/
resetHistory(context) {
context.commit('clearMessages');
context.commit('clearUtteranceStack');
context.dispatch('sendInitialUtterance');
context.commit('pushMessage', {
type: 'bot',
text: context.state.config.lex.initialText,
});
},
/** /SP-Edit */
/** ... */
/**
* SP-Addition
* requires restartButton to be enabled in config.json (an SP-Addition in lex-web-ui-config.json)
* renders Menu.Restartwhen this is enabled
*
* restartSession restarts the conversation session by:
*
* - Setting initialUtteranceSent to false
* - Clearing messages
* - Clearing utterance stack
* - Dispatching sendInitialUtterance after delay
* - Setting initialUtteranceSent to true
* - Creating new session ID in Lex
* - Pushing initial bot message
*
* sending initialText at the end does not appear to have consequence to this action
* as it appears: we never configure both (at dev discretion)
*
* !IMPORTANT does the same thing as the condition block in postTextMessage (but in a single run) to offer to user as a "Restart" button
* @see lex-web-ui/src/components/ToolbarContainer.vue
*/
restartSession(context) {
console.info('restartSession: setting initialUtteranceSent to false');
context.commit('setInitialUtteranceSent', false);
if (lexClient) {
console.info('Clearing messages', context.state.messages);
context.commit('clearMessages');
console.info('Messages cleared', context.state.messages);
console.info('Clearing utterance', context.state.utteranceStack);
context.dispatch('clearUtteranceStack');
console.info('Utterances cleared', context.state.utteranceStack);
console.info('Dispatching innitial utterance');
setTimeout(() => context.dispatch('sendInitialUtterance'), 500);
context.commit('setInitialUtteranceSent', true);
console.info('innitial utterance dispatched');
lexClient.createSessionId();
console.info('A new session id has been issued');
context.commit('pushMessage', {
type: 'bot',
text: context.state.config.lex.initialText,
});
}
},
/** /SP-Addition */
/** ... */
/**
* SP-Addition
* just a dispatch wrapper to commit to mutations of the same name
* unused
*/
clearUtteranceStack(context) {
context.commit('clearUtteranceStack');
},
/** /SP-Addition */
};
// lex-web-ui/src/lib/lex/client.js
import { v4 as uuidv4 } from "uuid";
/** ... */
export default class {
botV2Id;
botV2AliasId;
botV2LocaleId;
isV2Bot;
constructor({
botName,
botAlias = '$LATEST',
userId,
lexRuntimeClient,
botV2Id,
botV2AliasId,
botV2LocaleId,
lexRuntimeV2Client,
}) {
if (!botName || !lexRuntimeClient || !lexRuntimeV2Client ||
typeof botV2Id === 'undefined' ||
typeof botV2AliasId === 'undefined' ||
typeof botV2LocaleId === 'undefined'
) {
console.error(`botName: ${botName} botV2Id: ${botV2Id} botV2AliasId ${botV2AliasId} ` +
`botV2LocaleId ${botV2LocaleId} lexRuntimeClient ${lexRuntimeClient} ` +
`lexRuntimeV2Client ${lexRuntimeV2Client}`);
throw new Error('invalid lex client constructor arguments');
}
this.botName = botName;
this.botAlias = botAlias;
/**
* SP-Edit
* Initialize userId to a v4-compliant uuid
*/
this.userId = userId || uuidv4();
/** /SP-Edit */
this.botV2Id = botV2Id;
this.botV2AliasId = botV2AliasId;
this.botV2LocaleId = botV2LocaleId;
this.isV2Bot = (this.botV2Id.length > 0);
this.lexRuntimeClient = this.isV2Bot ? lexRuntimeV2Client : lexRuntimeClient;
this.credentials = this.lexRuntimeClient.config.credentials;
}
initCredentials(credentials) {
this.credentials = credentials;
this.lexRuntimeClient.config.credentials = this.credentials;
/**
* SP-Edit
* Do not set userId from Cognito
*/
this.userId = this.userId ? this.userId : uuidv4();
// this.userId = (credentials.identityId) ?
// credentials.identityId :
// this.userId;
/** /SP-Edit */
}
/** ... */
startNewSession() {
let putSessionReq;
/**
* SP-Edit
* content.length must be > 0 on init when dialogaction.type is ElicitIntent if doing it for v2
* set empty sessionState object instead
*
* @see {@link 'https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/lex-runtime-v2/command/PutSessionCommand/' PutSessionCommand}
*/
/** /SP-Edit */
if (this.isV2Bot) {
putSessionReq = this.lexRuntimeClient.putSession({
botAliasId: this.botV2AliasId,
botId: this.botV2Id,
localeId: this.botV2LocaleId,
sessionId: this.userId,
sessionState: {
},
});
} else {
putSessionReq = this.lexRuntimeClient.putSession({
botAlias: this.botAlias,
botName: this.botName,
userId: this.userId,
dialogAction: {
type: 'ElicitIntent',
},
});
}
return this.credentials.getPromise()
.then(creds => creds && this.initCredentials(creds))
.then(() => putSessionReq.promise());
}
/** ... */
/**
* SP-Addition
* Make sessionId v4-compliant
* @returns {string} new uuid v4 string
*/
createSessionId() {
console.info('createSessionId triggered: previous id', this.userId);
this.userId = uuidv4();
console.info('rcreatesessionid done new id:', this.userId);
}
/** /SP-Addition */
}
<!-- lex-web-ui/src/components/ToolbarContainer.vue -->
<!-- Ultimate Restart Button (user initiated) -->
<template>
<!-- eslint-disable max-len -->
<!-- v-toolbar -->
<!-- v-menu -->
<!-- v-list -->
<!-- if enableLogin -->
<!-- if isSaveHistory -->
<!-- etc -->
<!-- SP-Addition -->
<!-- Restart but as a button for user to trigger a restart action -->
<v-list-tile v-if="shouldRenderRestartButton">
<v-list-tile-title v-on:click="requestRestartSession" aria-label="restart session">
<v-icon>
{{ items[5].icon }}
</v-icon>
{{ items[5].title }}
</v-list-tile-title>
</v-list-tile>
<!-- /SP-Addition -->
<script>
import { chatMode, liveChatStatus } from '@/store/state';
export default {
/** ... */
computed: {
/** ... */
/**
* SP-Addition
* Offers an SP1-specific restart button.
*/
shouldRenderRestartButton() {
return this.$store.state.config.ui.restartButton;
},
/** /SP-Addition */
},
methods: {
/** ... */
/**
* SP-Edit
* dispatches an action to restart a session with logic to issue
* new credentials
* @see restartSession in lex-web-ui/src/store/actions.js
*/
requestRestartSession() {
this.$store.dispatch('restartSession');
},
/** /SP-Edit */
},
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment