@microsoft/teams-ai
Version:
SDK focused on building AI based applications for Microsoft Teams.
126 lines • 6 kB
JavaScript
;
/**
* @module teams-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConversationHistory = void 0;
const PromptSectionBase_1 = require("./PromptSectionBase");
const Utilities_1 = require("../Utilities");
/**
* A section that renders the conversation history.
*/
class ConversationHistory extends PromptSectionBase_1.PromptSectionBase {
variable;
userPrefix;
assistantPrefix;
/**
* Creates a new 'ConversationHistory' instance.
* @param {string} variable - Name of memory variable used to store the histories `Message[]`.
* @param {number} tokens - Optional. Sizing strategy for this section. Defaults to `proportional` with a value of `1.0`.
* @param {boolean} required - Optional. Indicates if this section is required. Defaults to `false`.
* @param {string} userPrefix - Optional. Prefix to use for user messages when rendering as text. Defaults to `user: `.
* @param {string} assistantPrefix - Optional. Prefix to use for assistant messages when rendering as text. Defaults to `assistant: `.
* @param {string} separator - Optional. Separator to use between messages when rendering as text. Defaults to `\n`.
*/
constructor(variable, tokens = 1.0, required = false, userPrefix = 'user: ', assistantPrefix = 'assistant: ', separator = '\n') {
super(tokens, required, separator);
this.variable = variable;
this.userPrefix = userPrefix;
this.assistantPrefix = assistantPrefix;
}
/**
* @param {TurnContext} context - Context for the current turn of conversation.
* @param {Memory} memory - Memory to use for rendering.
* @param {PromptFunctions} functions - Prompt functions to use for rendering.
* @param {Tokenizer} tokenizer - Tokenizer to use for encoding text.
* @param {number} maxTokens - Maximum number of tokens allowed.
* @returns {Promise<RenderedPromptSection<string>>} Rendered prompt section as a string.
* @private
*/
async renderAsText(context, memory, functions, tokenizer, maxTokens) {
// Get messages from memory
const history = (memory.getValue(this.variable) ?? []).slice();
// Populate history and stay under the token budget
let tokens = 0;
const budget = this.tokens > 1.0 ? Math.min(this.tokens, maxTokens) : maxTokens;
const separatorLength = tokenizer.encode(this.separator).length;
const lines = [];
for (let i = history.length - 1; i >= 0; i--) {
const msg = history[i];
const message = { role: msg.role, content: Utilities_1.Utilities.toString(tokenizer, msg.content) };
const prefix = message.role === 'user' ? this.userPrefix : this.assistantPrefix;
const line = prefix + message.content;
const length = tokenizer.encode(line).length + (lines.length > 0 ? separatorLength : 0);
// Add initial line if required
if (lines.length === 0 && this.required) {
tokens += length;
lines.unshift(line);
continue;
}
// Stop if we're over the token budget
if (tokens + length > budget) {
break;
}
// Add line
tokens += length;
lines.unshift(line);
}
return { output: lines.join(this.separator), length: tokens, tooLong: tokens > maxTokens };
}
/**
* @private
* @param {TurnContext} context - Context for the current turn of conversation.
* @param {Memory} memory - Memory to use for rendering.
* @param {PromptFunctions} functions - Prompt functions to use for rendering.
* @param {Tokenizer} tokenizer - Tokenizer to use for encoding text.
* @param {number} maxTokens - Maximum number of tokens allowed.
* @returns {Promise<RenderedPromptSection<Message[]>>} Rendered prompt section as a list of messages.
*/
async renderAsMessages(context, memory, functions, tokenizer, maxTokens) {
// Get messages from memory
const history = (memory.getValue(this.variable) ?? []).slice();
// Populate messages and stay under the token budget
let tokens = 0;
const budget = this.getTokenBudget(maxTokens);
const messages = [];
for (let i = history.length - 1; i >= 0; i--) {
// Clone message
const msg = history[i];
const message = Object.assign({}, msg);
if (msg.content !== null) {
message.content = Utilities_1.Utilities.toString(tokenizer, msg.content);
}
// Get text message length
let length = tokenizer.encode(PromptSectionBase_1.PromptSectionBase.getMessageText(message)).length;
// Add length of any image parts
// TODO: This accounts for low detail images but not high detail images.
if (Array.isArray(message.content)) {
length += message.content.filter((part) => part.type === 'image').length * 85;
}
// Add initial message if required
if (messages.length === 0 && this.required) {
tokens += length;
messages.unshift(message);
continue;
}
// Stop if we're over the token budget
if (tokens + length > budget) {
break;
}
// Add message
tokens += length;
messages.unshift(message);
}
// Remove completed partial action outputs
while (messages.length > 0 && messages[0].role === 'tool') {
messages.shift();
}
return { output: messages, length: tokens, tooLong: tokens > maxTokens };
}
}
exports.ConversationHistory = ConversationHistory;
//# sourceMappingURL=ConversationHistory.js.map