Skip to content
This repository was archived by the owner on Oct 21, 2024. It is now read-only.

Commit ecc219f

Browse files
Merge pull request #11 from artificialsolutions/breakpoints
Breakpoints
2 parents 8606cc0 + 6e06c71 commit ecc219f

File tree

4 files changed

+174
-87
lines changed

4 files changed

+174
-87
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ If we look at Microsoft's specification of an [image attachment](https://docs.mi
6969
}
7070
```
7171

72+
## Splitting answers into 'bubbles'
73+
Sometimes you may wish to provide an answer using multiple text bubbles. This can be achieved by including an output parameter called `outputTextSegmentIndexes`. This output parameter should contain a list of index pairs, to indicate where the output text can be split into separate bubbles. The value of the `outputTextSegmentIndexes` should be structured like this (linebreaks are added for readability):
74+
```
75+
[
76+
[startIndexOfFirstBubble, endIndexOfFirstBubble],
77+
[startIndexOfSecondBubble, endIndexOfSecondBubble],
78+
...
79+
]
80+
```
81+
82+
For more details on how to generate the value of `outputTextSegmentIndexes` in Teneo Studio, please refer to [Splitting answers into bubbles](https://www.teneo.ai/engine/channels/microsoft-bot-framework#splitting-answers-into-bubbles).
83+
7284
## Engine input parameters
7385
### channel
7486
The input parameter `channel` allows you to add channel specfic optimisations to your bot. The value start with `botframework-` and the botframework channel is appended. For example, the value for requests from users that use Teams is `botframework-msteams`.

bot.js

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
const { ActivityTypes } = require('botbuilder');
1818
const TIE = require('@artificialsolutions/tie-api-client');
19+
const TENEO_OUTPUTSEGMENTS_PARAM = "outputTextSegmentIndexes";
1920
const dotenv = require('dotenv');
2021
dotenv.config();
2122

@@ -93,7 +94,7 @@ class MyBot {
9394
if (message.text) {
9495
messageText = message.text;
9596
}
96-
97+
9798
console.log(`Got message '${messageText}' from channel ${message.channelId}`);
9899

99100
// find engine session id
@@ -115,39 +116,105 @@ class MyBot {
115116

116117
// store egnine sessionId in conversation state
117118
await this.sessionIdProperty.set(turnContext, teneoResponse.sessionId);
118-
119-
const reply = [];
120119

121-
// set reply text to answer text from engine
122-
reply.text = teneoResponse.output.text;
123-
124-
// check if an output parameter 'msbotframework' exists in engine response
125-
// if so, check if it should be added as attachment/card or suggestion action
126-
if (teneoResponse.output.parameters.msbotframework) {
127-
try {
128-
const extension = JSON.parse(teneoResponse.output.parameters.msbotframework);
129-
130-
// suggested actions have an 'actions' key
131-
if (extension.actions) {
132-
reply.suggestedActions = extension
133-
} else {
134-
// we assume the extension code matches that of an attachment or rich card
135-
reply.attachments = [extension];
120+
// separate the answer text into segments if applicable
121+
let answerTextSegments = this.getOutputTextSegments(teneoResponse);
122+
123+
for (let index = 0; index < answerTextSegments.length; index++) {
124+
const segmentText = answerTextSegments[index];
125+
const reply = {};
126+
127+
reply.text = segmentText;
128+
129+
// only send bot framework actions for the last segment/message
130+
if (index + 1 === answerTextSegments.length) {
131+
// check if an output parameter 'msbotframework' exists in engine response
132+
// if so, check if it should be added as attachment/card or suggestion action
133+
if (teneoResponse.output.parameters.msbotframework) {
134+
try {
135+
const extension = JSON.parse(
136+
teneoResponse.output.parameters.msbotframework
137+
);
138+
139+
// suggested actions have an 'actions' key
140+
if (extension.actions) {
141+
reply.suggestedActions = extension;
142+
} else {
143+
// we assume the extension code matches that of an attachment or rich card
144+
reply.attachments = [extension];
145+
}
146+
} catch (attachError) {
147+
console.error(`Failed when parsing attachment JSON`, attachError);
148+
}
136149
}
137-
} catch (error_attach) {
138-
console.error(`Failed when parsing attachment JSON`, error_attach);
139150
}
140-
}
141151

142-
// send response to bot framework.
143-
await turnContext.sendActivity(reply);
152+
// send response to bot framework.
153+
await turnContext.sendActivity(reply);
154+
}
144155

145-
146156
} catch (error) {
147157
console.error(`Failed when sending input to Teneo Engine @ ${teneoEngineUrl}`, error);
148158
}
149159

150160
}
161+
162+
/**
163+
* A Teneo response can contain an output parameter that
164+
* indicates how the output text can be split in multiple
165+
* segments or messages. The value of the parameter is a list
166+
* with start and end index pairs that looks like this:
167+
* [[0, 39], [40, 67], [70, 96]]
168+
* getOutputTextSegments will split the output text into
169+
* its segments and return them as a list of strings.
170+
**/
171+
getOutputTextSegments(teneoResponse) {
172+
173+
const teneoAnswerText = teneoResponse.output.text;
174+
let segments = [];
175+
let segmentRanges;
176+
177+
// get the output param with boundaries for each message
178+
try {
179+
const segmentsString = teneoResponse.output.parameters[TENEO_OUTPUTSEGMENTS_PARAM];
180+
if (segmentsString) {
181+
segmentRanges = JSON.parse(segmentsString);
182+
}
183+
} catch(err) {
184+
console.log('Error: Unable to parse segmentsString JSON')
185+
}
186+
187+
if (segmentRanges && Array.isArray(segmentRanges)) {
188+
189+
// each segmentRange in the list contains the start and end index for a message
190+
segmentRanges.forEach((segmentRange) => {
191+
try {
192+
// get the start and end index for this segment
193+
var segmentStartIndex = segmentRange[0];
194+
var segmentEndIndex = segmentRange[1];
195+
196+
// if start and end seem valid
197+
if (!isNaN(segmentStartIndex) && !isNaN(segmentEndIndex)) {
198+
// get the substring from the answer text that needs to appear in a bubble
199+
var segmentText = teneoAnswerText.substring(segmentStartIndex,segmentEndIndex).trim();
200+
201+
// add the segment to the list of segments, but only if it is not empty
202+
if (segmentText) {
203+
segments.push(segmentText)
204+
}
205+
}
206+
207+
} catch (err) {
208+
console.log('Error: unexpected segment range')
209+
}
210+
});
211+
} else {
212+
// message does not need to be segmented, the only chunk is the full answer text
213+
segments.push(teneoAnswerText)
214+
}
215+
return segments;
216+
}
217+
151218
}
152219

153220
module.exports.MyBot = MyBot;

0 commit comments

Comments
 (0)