Skip to content

Commit 5e11a9b

Browse files
committed
add heartbeat for streaming responses to prevent idle timeout of 10 mins
1 parent de518e5 commit 5e11a9b

File tree

8 files changed

+109
-220
lines changed

8 files changed

+109
-220
lines changed

dist/index.html

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1 @@
1-
<!DOCTYPE html>
2-
<html>
3-
<head>
4-
<meta charset="utf-8">
5-
<!-- Frame and Image source CSP is controlled by Cloudfront headers to allow customization via Cloudformation -->
6-
<meta http-equiv="Content-Security-Policy"
7-
content="default-src 'self' blob: data:;
8-
worker-src blob: data:;
9-
img-src * data:;
10-
frame-src *;
11-
script-src 'self' 'unsafe-eval';
12-
style-src 'self' 'unsafe-inline';
13-
connect-src 'self' wss://*.amazonaws.com *.amazonaws.com *.amazoncognito.com;
14-
font-src 'self' fonts.gstatic.com;
15-
upgrade-insecure-requests;">
16-
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
17-
<title>LexWebUi</title>
18-
<!-- empty favicon to avoid 404 -->
19-
<link href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=" rel="icon" type="image/x-icon" />
20-
21-
<!--
22-
Webpack injects the loader css tag here.
23-
if you want to include it manually in your app,
24-
replace the webpack tag with:
25-
<link href="lex-web-ui-loader.min.css" rel="stylesheet">
26-
-->
27-
<link href="lex-web-ui-loader.css" rel="stylesheet">
28-
29-
<link rel="stylesheet" href="./custom-chatbot-style.css">
30-
</head>
31-
<body>
32-
<!--
33-
Loader script.
34-
Creates a global variable named ChatbotUiLoader.
35-
36-
Webpack injects the loader script tag here.
37-
if you want to include it manually in your app,
38-
replace the webpack tag with:
39-
<script src="./lex-web-ui-loader.min.js"></script>
40-
-->
41-
<script src="lex-web-ui-loader.js"></script>
42-
43-
<!--
44-
The following script instantiate the full page loader and
45-
calls its load function.
46-
-->
47-
<script src="./initiate-loader.js"></script>
48-
</body>
49-
</html>
1+
<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'self' blob: data:; worker-src blob: data:; img-src * data:; frame-src *; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' wss://*.amazonaws.com *.amazonaws.com *.amazoncognito.com; font-src 'self' fonts.gstatic.com; upgrade-insecure-requests;"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,minimal-ui"><title>LexWebUi</title><link href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=" rel="icon" type="image/x-icon"/><link href="lex-web-ui-loader.min.css" rel="stylesheet"><link rel="stylesheet" href="./custom-chatbot-style.css"></head><body><script src="lex-web-ui-loader.min.js"></script><script src="./initiate-loader.js"></script></body></html>

dist/lex-web-ui.js

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50216,7 +50216,7 @@ __webpack_require__.r(__webpack_exports__);
5021650216
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
5021750217
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
5021850218
/* harmony export */ });
50219-
/* harmony import */ var _home_ec2_user_environment_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/defineProperty.js */ "./node_modules/@babel/runtime/helpers/esm/defineProperty.js");
50219+
/* harmony import */ var _Users_abhirpat_Documents_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/defineProperty.js */ "./node_modules/@babel/runtime/helpers/esm/defineProperty.js");
5022050220
/* harmony import */ var core_js_modules_es_array_push_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/es.array.push.js */ "./node_modules/core-js/modules/es.array.push.js");
5022150221
/* harmony import */ var core_js_modules_es_array_push_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_push_js__WEBPACK_IMPORTED_MODULE_1__);
5022250222
/* harmony import */ var core_js_modules_esnext_iterator_constructor_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/esnext.iterator.constructor.js */ "./node_modules/core-js/modules/esnext.iterator.constructor.js");
@@ -50264,10 +50264,10 @@ function compressAndB64Encode(src) {
5026450264
botV2LocaleId,
5026550265
lexRuntimeV2Client
5026650266
}) {
50267-
(0,_home_ec2_user_environment_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2Id", void 0);
50268-
(0,_home_ec2_user_environment_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2AliasId", void 0);
50269-
(0,_home_ec2_user_environment_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2LocaleId", void 0);
50270-
(0,_home_ec2_user_environment_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "isV2Bot", void 0);
50267+
(0,_Users_abhirpat_Documents_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2Id", void 0);
50268+
(0,_Users_abhirpat_Documents_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2AliasId", void 0);
50269+
(0,_Users_abhirpat_Documents_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "botV2LocaleId", void 0);
50270+
(0,_Users_abhirpat_Documents_aws_lex_web_ui_lex_web_ui_node_modules_babel_runtime_helpers_esm_defineProperty_js__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "isV2Bot", void 0);
5027150271
if (!botName || !lexRuntimeClient || !lexRuntimeV2Client || typeof botV2Id === 'undefined' || typeof botV2AliasId === 'undefined' || typeof botV2LocaleId === 'undefined') {
5027250272
console.error(`botName: ${botName} botV2Id: ${botV2Id} botV2AliasId ${botV2AliasId} ` + `botV2LocaleId ${botV2LocaleId} lexRuntimeClient ${lexRuntimeClient} ` + `lexRuntimeV2Client ${lexRuntimeV2Client}`);
5027350273
throw new Error('invalid lex client constructor arguments');
@@ -51786,11 +51786,11 @@ let pollyThereWasAnErrorBlob = {};
5178651786
context.commit('setIsStartingTypingWsMessages', true);
5178751787
wsClient.onmessage = event => {
5178851788
if (event.data !== '/stop/' && context.getters.isStartingTypingWsMessages()) {
51789-
console.info("streaming ", context.getters.isStartingTypingWsMessages());
51789+
console.info('Streaming: ', context.getters.isStartingTypingWsMessages());
5179051790
context.commit('pushWebSocketMessage', event.data);
5179151791
context.dispatch('typingWsMessages');
5179251792
} else {
51793-
console.info('stopping streaming');
51793+
console.info('Currently not streaming');
5179451794
}
5179551795
};
5179651796
}
@@ -52323,6 +52323,40 @@ let pollyThereWasAnErrorBlob = {};
5232352323
};
5232452324
const signedUrl = aws_amplify__WEBPACK_IMPORTED_MODULE_19__.Signer.signUrl(context.state.config.lex.streamingWebSocketEndpoint + '?sessionId=' + sessionId, accessInfo, serviceInfo);
5232552325
wsClient = new WebSocket(signedUrl);
52326+
52327+
// Add heartbeat logic
52328+
const HEARTBEAT_INTERVAL = 540000; // 9 minutes
52329+
const MAX_DURATION = 7200000; // 2 hours
52330+
const startTime = Date.now();
52331+
let heartbeatTimer = null;
52332+
function startHeartbeat() {
52333+
if (wsClient.readyState === WebSocket.OPEN) {
52334+
const elapsedTime = Date.now() - startTime;
52335+
if (elapsedTime < MAX_DURATION) {
52336+
const pingMessage = JSON.stringify({
52337+
action: 'ping'
52338+
});
52339+
wsClient.send(pingMessage);
52340+
console.log('Sending Ping:', new Date().toISOString());
52341+
heartbeatTimer = setTimeout(startHeartbeat, HEARTBEAT_INTERVAL);
52342+
} else {
52343+
console.log('Stopped sending pings after reaching 2-hour limit.');
52344+
clearTimeout(heartbeatTimer);
52345+
}
52346+
}
52347+
}
52348+
wsClient.onopen = () => {
52349+
console.log('WebSocket Connected');
52350+
startHeartbeat();
52351+
};
52352+
wsClient.onclose = () => {
52353+
console.log('WebSocket Closed');
52354+
clearTimeout(heartbeatTimer);
52355+
};
52356+
wsClient.onerror = error => {
52357+
console.log('WebSocket Error', error.message);
52358+
clearTimeout(heartbeatTimer);
52359+
};
5232652360
},
5232752361
typingWsMessages(context) {
5232852362
if (context.getters.wsMessagesCurrentIndex() < context.getters.wsMessagesLength() - 1) {

dist/lex-web-ui.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/lex-web-ui.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/parent.html

Lines changed: 3 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,4 @@
1-
<!DOCTYPE html>
2-
<html>
3-
<head>
4-
<meta charset="utf-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
7-
<title>Chatbot UI iFrame Example</title>
8-
<!-- bootstrap CSS -->
9-
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
10-
</head>
11-
<body>
12-
<div class="container-fluid"><!-- main container -->
13-
<h1>Lex Chatbot UI Sample Parent Page</h1>
14-
<p class="lead">
15-
Use the chatbot UI to drive your bot. This page will dynamically
16-
update using the associated Lex variables as the bot dialog progresses.
17-
</p>
18-
19-
<div class="row">
20-
<div class="col-md-6">
21-
<div class="panel panel-primary">
22-
<div class="panel-heading">Send "Buy Flowers" utterance to iframe</div>
23-
<div class="panel-body">
24-
<div>
25-
<button id="send-intent" type="button" class="btn btn-default" disabled>
26-
Send
27-
</button>
28-
</div>
29-
</div>
30-
</div>
31-
</div>
32-
</div>
33-
34-
<div class="row">
35-
<div class="col-md-6">
36-
<div class="panel panel-primary">
37-
<div class="panel-heading">Lex Dialog State</div>
38-
<div class="panel-body">
39-
<div>
40-
<strong>dialogState:</strong>
41-
<span id="dialog-state"></span>
42-
</div>
43-
</div>
44-
</div>
45-
</div>
46-
</div>
47-
48-
<div class="row">
49-
<div class="col-md-6">
50-
<div class="panel panel-primary">
51-
<div class="panel-heading">Lex Slots</div>
52-
<div class="panel-body">
53-
<div id="slots"></div>
54-
</div>
55-
</div>
56-
</div>
57-
</div>
58-
59-
<div class="row">
60-
<div class="col-md-6">
61-
<div class="panel panel-primary">
62-
<div class="panel-heading">Lex Intent Name</div>
63-
<div class="panel-body">
64-
<div>
65-
<strong>Intent Name:</strong>
66-
<span id="intent-name"></span>
67-
</div>
68-
</div>
69-
</div>
70-
</div>
71-
</div>
72-
73-
<div class="row">
74-
<div class="col-md-6">
75-
<div class="panel panel-primary">
76-
<div class="panel-heading">Lex Session Attributes</div>
77-
<div class="panel-body">
78-
<div>
79-
<pre id="session-attributes"></pre>
80-
</div>
81-
</div>
82-
</div>
83-
</div>
84-
</div>
85-
86-
<div class="row">
87-
<div class="col-md-6">
88-
<div class="panel panel-primary">
89-
<div class="panel-heading">Lex Response Card</div>
90-
<div class="panel-body">
91-
<div>
92-
<pre id="response-card"></pre>
93-
</div>
94-
</div>
95-
</div>
96-
</div>
97-
</div>
98-
99-
<div class="row">
100-
<div class="col-md-6">
101-
<div class="panel panel-warning">
102-
<div class="panel-heading">NOTE</div>
103-
<div class="panel-body">
104-
<p>
105-
This application was created using the
106-
<a href="https://github.com/awslabs/aws-lex-web-ui">aws-lex-web-ui</a>
107-
project.
108-
</p>
109-
</div>
110-
</div>
111-
</div>
112-
</div>
113-
</div><!-- main container -->
114-
115-
<!-- jquery used as part on this example - not required by lex-web-ui -->
116-
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
117-
<!-- bootstrap used as part of thie example - not required by lex-web-ui -->
118-
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
119-
120-
<!--
121-
Loader script tag
122-
123-
Add a script tag pointing to the loader library towards the bottom
124-
of the html BODY section
125-
126-
The library and its dependencies can be hosted on another site,
127-
S3 bucket or a CDN (e.g. CloudFront)
128-
129-
This script creates a global variable named ChatbotUiLoader which
130-
provides the loader library functionality
131-
132-
Webpack injects the loader script tag here.
133-
if you want to include it manually in your app,
134-
replace the webpack tag with:
135-
<script src="./lex-web-ui-loader.min.js"></script>
136-
-->
137-
<script src="lex-web-ui-loader.js"></script>
138-
139-
<!--
140-
After the loader script tag has been included, you can use its global
141-
variable to load the chatbot UI in an iframe
142-
-->
143-
<script>
144-
145-
// The loader constructor supports various configurable options used to
1+
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo="><title>Chatbot UI iFrame Example</title><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></head><body><div class="container-fluid"><h1>Lex Chatbot UI Sample Parent Page</h1><p class="lead">Use the chatbot UI to drive your bot. This page will dynamically update using the associated Lex variables as the bot dialog progresses.</p><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Send "Buy Flowers" utterance to iframe</div><div class="panel-body"><div><button id="send-intent" type="button" class="btn btn-default" disabled="disabled">Send</button></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Lex Dialog State</div><div class="panel-body"><div><strong>dialogState:</strong> <span id="dialog-state"></span></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Lex Slots</div><div class="panel-body"><div id="slots"></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Lex Intent Name</div><div class="panel-body"><div><strong>Intent Name:</strong> <span id="intent-name"></span></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Lex Session Attributes</div><div class="panel-body"><div><pre id="session-attributes"></pre></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-primary"><div class="panel-heading">Lex Response Card</div><div class="panel-body"><div><pre id="response-card"></pre></div></div></div></div></div><div class="row"><div class="col-md-6"><div class="panel panel-warning"><div class="panel-heading">NOTE</div><div class="panel-body"><p>This application was created using the <a href="https://github.com/awslabs/aws-lex-web-ui">aws-lex-web-ui</a> project.</p></div></div></div></div></div><script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script><script src="lex-web-ui-loader.min.js"></script><script>// The loader constructor supports various configurable options used to
1462
// control how the component configuration and dependencies are retrieved.
1473
var loaderOpts = {
1484
// Point the baseUrl option to the the base URL used to download
@@ -230,15 +86,7 @@ <h1>Lex Chatbot UI Sample Parent Page</h1>
23086
})
23187
.then(function () { console.log('message succesfully sent'); })
23288
.catch(function (err) { console.error('error sending message ', err); });
233-
}
234-
</script>
235-
236-
<!--
237-
This script illustrates how to handle the various events supported by the
238-
chatbot UI.
239-
-->
240-
<script>
241-
$(document).ready(function chatbotHandler() {
89+
}</script><script>$(document).ready(function chatbotHandler() {
24290

24391
// When the chatbot ui iframe is ready to receive the
24492
// dynamic config it sends the 'receivelexconfig' event to the parent
@@ -333,7 +181,4 @@ <h1>Lex Chatbot UI Sample Parent Page</h1>
333181

334182
$('#slots').replaceWith($slotsContainerReplacement);
335183
});
336-
});
337-
</script>
338-
</body>
339-
</html>
184+
});</script></body></html>

lex-web-ui/src/store/actions.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,11 +710,11 @@ export default {
710710

711711
wsClient.onmessage = (event) => {
712712
if(event.data!=='/stop/' && context.getters.isStartingTypingWsMessages()){
713-
console.info("streaming ", context.getters.isStartingTypingWsMessages());
713+
console.info('Streaming: ', context.getters.isStartingTypingWsMessages());
714714
context.commit('pushWebSocketMessage',event.data);
715715
context.dispatch('typingWsMessages')
716716
}else{
717-
console.info('stopping streaming');
717+
console.info('Currently not streaming');
718718
}
719719
}
720720
}
@@ -1304,6 +1304,41 @@ export default {
13041304

13051305
const signedUrl = Signer.signUrl(context.state.config.lex.streamingWebSocketEndpoint+'?sessionId='+sessionId, accessInfo, serviceInfo);
13061306
wsClient = new WebSocket(signedUrl);
1307+
1308+
// Add heartbeat logic
1309+
const HEARTBEAT_INTERVAL = 540000; // 9 minutes
1310+
const MAX_DURATION = 7200000; // 2 hours
1311+
const startTime = Date.now();
1312+
let heartbeatTimer = null;
1313+
1314+
function startHeartbeat() {
1315+
if (wsClient.readyState === WebSocket.OPEN) {
1316+
const elapsedTime = Date.now() - startTime;
1317+
if (elapsedTime < MAX_DURATION) {
1318+
const pingMessage = JSON.stringify({ action: 'ping' });
1319+
wsClient.send(pingMessage);
1320+
console.log('Sending Ping:', new Date().toISOString());
1321+
heartbeatTimer = setTimeout(startHeartbeat, HEARTBEAT_INTERVAL);
1322+
} else {
1323+
console.log('Stopped sending pings after reaching 2-hour limit.');
1324+
clearTimeout(heartbeatTimer);
1325+
}
1326+
}
1327+
}
1328+
wsClient.onopen = () => {
1329+
console.log('WebSocket Connected');
1330+
startHeartbeat();
1331+
};
1332+
1333+
wsClient.onclose = () => {
1334+
console.log('WebSocket Closed');
1335+
clearTimeout(heartbeatTimer);
1336+
};
1337+
1338+
wsClient.onerror = (error) => {
1339+
console.log('WebSocket Error', error.message);
1340+
clearTimeout(heartbeatTimer);
1341+
};
13071342
},
13081343
typingWsMessages(context){
13091344
if (context.getters.wsMessagesCurrentIndex()<context.getters.wsMessagesLength()-1){

0 commit comments

Comments
 (0)