@microsoft/botbuilder-m365
Version:
M365 extensions for Microsoft BotBuilder, Alpha release.
255 lines • 10.9 kB
JavaScript
;
/**
* @module botbuilder-m365
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConversationHistory = void 0;
// TODO:
/* eslint-disable security/detect-object-injection */
const gpt_3_encoder_1 = require("gpt-3-encoder");
class ConversationHistory {
/**
* Adds a new line of text to conversation history
*
* @param {TurnState} state Applications turn state.
* @param {string} line Line of text to add to history.
* @param {number} maxLines Optional. Maximum number of lines to store. Defaults to 10.
*/
static addLine(state, line, maxLines = 10) {
if (state.conversation) {
// Create history array if it doesn't exist
let history = state.conversation.value[ConversationHistory.StatePropertyName];
if (!Array.isArray(history)) {
history = [];
}
// Add line to history
history.push(line);
// Prune history if too long
if (history.length > maxLines) {
history.splice(0, history.length - maxLines);
}
// Save history back to conversation state
state.conversation.value[ConversationHistory.StatePropertyName] = history;
}
else {
throw new Error(`ConversationHistory.addLine() was passed a state object without a 'conversation' state member.`);
}
}
static appendToLastLine(state, text) {
const line = ConversationHistory.getLastLine(state);
ConversationHistory.replaceLastLine(state, line + text);
}
static clear(state) {
if (state.conversation) {
state.conversation.value[ConversationHistory.StatePropertyName] = [];
}
else {
throw new Error(`ConversationHistory.clear() was passed a state object without a 'conversation' state member.`);
}
}
static hasMoreLines(state) {
if (state.conversation) {
const history = state.conversation.value[ConversationHistory.StatePropertyName];
return Array.isArray(history) ? history.length > 0 : false;
}
else {
throw new Error(`ConversationHistory.hasMoreLines() was passed a state object without a 'conversation' state member.`);
}
}
static getLastLine(state) {
if (state.conversation) {
// Create history array if it doesn't exist
let history = state.conversation.value[ConversationHistory.StatePropertyName];
if (!Array.isArray(history)) {
history = [];
}
// Return the last line or an empty string
return history.length > 0 ? history[history.length - 1] : '';
}
else {
throw new Error(`ConversationHistory.getLastLine() was passed a state object without a 'conversation' state member.`);
}
}
static getLastSay(state) {
// Find start of text
const lastLine = this.getLastLine(state);
let textPos = lastLine.lastIndexOf(' SAY ');
if (textPos >= 0) {
textPos += 5;
}
else {
// Find end of prefix
textPos = lastLine.indexOf(': ');
if (textPos >= 0) {
textPos += 2;
}
else {
// Just use whole text
textPos = 0;
}
}
// Trim off any DO statements
let text = lastLine.substring(textPos);
let doPos = text.indexOf(' THEN DO ');
if (doPos >= 0) {
text = text.substring(0, doPos);
}
else {
doPos = text.indexOf(' DO ');
if (doPos >= 0) {
text = text.substring(0, doPos);
}
}
return text.indexOf('DO ') < 0 ? text.trim() : '';
}
static removeLastLine(state) {
if (state.conversation) {
// Create history array if it doesn't exist
let history = state.conversation.value[ConversationHistory.StatePropertyName];
if (!Array.isArray(history)) {
history = [];
}
// Remove last line
// - undefined is returned if the array is empty
const line = history.pop();
// Save history back to conversation state
state.conversation.value[ConversationHistory.StatePropertyName] = history;
// Return removed line
return line;
}
else {
throw new Error(`ConversationHistory.removeLastLine() was passed a state object without a 'conversation' state member.`);
}
}
static replaceLastLine(state, line) {
if (state.conversation) {
// Create history array if it doesn't exist
let history = state.conversation.value[ConversationHistory.StatePropertyName];
if (!Array.isArray(history)) {
history = [];
}
// Replace the last line or add a new one
if (history.length > 0) {
history[history.length - 1] = line;
}
else {
history.push(line);
}
// Save history back to conversation state
state.conversation.value[ConversationHistory.StatePropertyName] = history;
}
else {
throw new Error(`ConversationHistory.replaceLastLine() was passed a state object without a 'conversation' state member.`);
}
}
static replaceLastSay(state, newResponse, botPrefix = 'AI: ') {
if (state.conversation) {
// Create history array if it doesn't exist
let history = state.conversation.value[ConversationHistory.StatePropertyName];
if (!Array.isArray(history)) {
history = [];
}
// Update the last line or add a new one
if (history.length > 0) {
const line = history[history.length - 1];
const lastSayPos = line.lastIndexOf(' SAY ');
if (lastSayPos >= 0 && line.indexOf(' DO ', lastSayPos) < 0) {
// We found the last SAY and it wasn't followed by a DO
history[history.length - 1] = `${line.substring(0, lastSayPos)} SAY ${newResponse}`;
}
else if (line.indexOf(' DO ') >= 0) {
// Append a THEN SAY after the DO's
history[history.length - 1] = `${line} THEN SAY ${newResponse}`;
}
else {
// Just replace the entire line
history[history.length - 1] = `${botPrefix}${newResponse}`;
}
}
else {
history.push(`${botPrefix}${newResponse}`);
}
// Save history back to conversation state
state.conversation.value[ConversationHistory.StatePropertyName] = history;
}
else {
throw new Error(`ConversationHistory.replaceLastSay() was passed a state object without a 'conversation' state member.`);
}
}
/**
* Returns the current conversation history as a string of text.
*
*
* The length of the returned text is gated by `maxCharacterLength` and only whole lines of
* history entries will be returned. That means that if the length of the most recent history
* entry is greater then `maxCharacterLength` no text will be returned.
*
* @param {TurnState} state Applications turn state.
* @param {number} maxTokens Optional. Maximum length of the text returned. Defaults to 1000 tokens.
* @param {string} lineSeparator Optional. Separator used between lines. Defaults to '\n'.
* @returns {string} The most recent lines of conversation history as a text string.
*/
static toString(state, maxTokens = 1000, lineSeparator = '\n') {
var _a;
if (state.conversation) {
// Get history array if it exists
const history = (_a = state.conversation.value[ConversationHistory.StatePropertyName]) !== null && _a !== void 0 ? _a : [];
// Populate up to max chars
let text = '';
let textTokens = 0;
const lineSeparatorTokens = (0, gpt_3_encoder_1.encode)(lineSeparator).length;
for (let i = history.length - 1; i >= 0; i--) {
// Ensure that adding line won't go over the max character length
const line = history[i];
const lineTokens = (0, gpt_3_encoder_1.encode)(line).length;
const newTextTokens = textTokens + lineTokens + lineSeparatorTokens;
if (newTextTokens > maxTokens) {
break;
}
// Prepend line to output
text = `${line}${lineSeparator}${text}`;
textTokens = newTextTokens;
}
return text.trim();
}
else {
throw new Error(`ConversationHistory.toString() was passed a state object without a 'conversation' state member.`);
}
}
static toArray(state, maxTokens = 1000) {
var _a;
if (state.conversation) {
// Get history array if it exists
const history = (_a = state.conversation.value[ConversationHistory.StatePropertyName]) !== null && _a !== void 0 ? _a : [];
// Populate up to max chars
let textTokens = 0;
const lines = [];
for (let i = history.length - 1; i >= 0; i--) {
// Ensure that adding line won't go over the max character length
const line = history[i];
const lineTokens = (0, gpt_3_encoder_1.encode)(line).length;
const newTextTokens = textTokens + lineTokens;
if (newTextTokens > maxTokens) {
break;
}
// Prepend line to output
textTokens = newTextTokens;
lines.unshift(line);
}
return lines;
}
else {
throw new Error(`ConversationHistory.toArray() was passed a state object without a 'conversation' state member.`);
}
}
}
exports.ConversationHistory = ConversationHistory;
/**
* Name of the conversation state property used to hold the list of entires.
*/
ConversationHistory.StatePropertyName = '__history__';
//# sourceMappingURL=ConversationHistory.js.map