@microsoft/teams-ai
Version:
SDK focused on building AI based applications for Microsoft Teams.
223 lines • 8.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.TemplateSection = void 0;
const PromptSectionBase_1 = require("./PromptSectionBase");
const Utilities_1 = require("../Utilities");
/**
* A template section that will be rendered as a message.
* @remarks
* This section type is used to render a template as a message. The template can contain
* parameters that will be replaced with values from memory or call functions to generate
* dynamic content.
*
* Template syntax:
* - `{{$memoryKey}}` - Renders the value of the specified memory key.
* - `{{functionName}}` - Calls the specified function and renders the result.
* - `{{functionName arg1 arg2 ...}}` - Calls the specified function with the provided list of arguments.
*
* Function arguments are optional and separated by spaces. They can be quoted using `'`, `"`, or `\`` delimiters.
*/
class TemplateSection extends PromptSectionBase_1.PromptSectionBase {
_parts = [];
template;
role;
/**
* Creates a new 'TemplateSection' instance.
* @param {string} template - Template to use for this section.
* @param {string} role - Message role to use for this section.
* @param {number} tokens - Optional. Sizing strategy for this section. Defaults to `auto`.
* @param {boolean} required - Optional. Indicates if this section is required. Defaults to `true`.
* @param {string} separator - Optional. Separator to use between sections when rendering as text. Defaults to `\n`.
* @param {string} textPrefix - Optional. Prefix to use for text output. Defaults to `undefined`.
*/
constructor(template, role, tokens = -1, required = true, separator = '\n', textPrefix) {
super(tokens, required, separator, textPrefix);
this.template = template;
this.role = role;
this.parseTemplate();
}
/**
* @private
* @param {TurnContext} context - Context for the current turn of conversation with the user.
* @param {Memory} memory - An interface for accessing state values.
* @param {PromptFunctions} functions - An interface for calling functions.
* @param {Tokenizer} tokenizer - Tokenizer to use when rendering the section.
* @param {number} maxTokens - Maximum number of tokens allowed to be rendered.
* @returns {Promise<RenderedPromptSection<Message[]>>} A promise that resolves to the rendered section.
*/
async renderAsMessages(context, memory, functions, tokenizer, maxTokens) {
// Render parts in parallel
const renderedParts = await Promise.all(this._parts.map((part) => part(context, memory, functions, tokenizer, maxTokens)));
// Join all parts
const text = renderedParts.join('');
const length = tokenizer.encode(text).length;
// Return output
const messages = length > 0 ? [{ role: this.role, content: text }] : [];
return this.returnMessages(messages, length, tokenizer, maxTokens);
}
/**
* @private
*/
parseTemplate() {
// Parse template
let part = '';
let state = ParseState.inText;
let stringDelim = '';
for (let i = 0; i < this.template.length; i++) {
const char = this.template[i];
switch (state) {
case ParseState.inText:
if (char === '{' && this.template[i + 1] === '{') {
if (part.length > 0) {
this._parts.push(this.createTextRenderer(part));
part = '';
}
state = ParseState.inParameter;
i++;
}
else {
part += char;
}
break;
case ParseState.inParameter:
if (char === '}' && this.template[i + 1] === '}') {
if (part.length > 0) {
part = part.trim();
if (part[0] === '$') {
this._parts.push(this.createVariableRenderer(part.substring(1)));
}
else {
this._parts.push(this.createFunctionRenderer(part));
}
part = '';
}
state = ParseState.inText;
i++;
}
else if (["'", '"', '`'].includes(char)) {
stringDelim = char;
state = ParseState.inString;
part += char;
}
else {
part += char;
}
break;
case ParseState.inString:
part += char;
if (char === stringDelim) {
state = ParseState.inParameter;
}
break;
}
}
// Ensure we ended in the correct state
if (state !== ParseState.inText) {
throw new Error(`Invalid template: ${this.template}`);
}
// Add final part
if (part.length > 0) {
this._parts.push(this.createTextRenderer(part));
}
}
/**
* @private
* @param {string} text - Text to render.
* @returns {PartRenderer} A renderer that will render the specified text.
*/
createTextRenderer(text) {
return (context, memory, functions, tokenizer, maxTokens) => {
return Promise.resolve(text);
};
}
/**
* @private
* @param {string} name - Name of the variable to render.
* @returns {PartRenderer} A renderer that will render the specified variable.
*/
createVariableRenderer(name) {
return (context, memory, functions, tokenizer, maxTokens) => {
const value = memory.getValue(name);
return Promise.resolve(Utilities_1.Utilities.toString(tokenizer, value));
};
}
/**
* @private
* @param {string} param - Function to render.
* @returns {PartRenderer} A renderer that will render the specified function.
*/
createFunctionRenderer(param) {
let name = '';
const args = [];
/**
*
*/
function savePart() {
if (part.length > 0) {
if (!name) {
name = part;
}
else {
args.push(part);
}
part = '';
}
}
// Parse function name and args
let part = '';
let state = ParseState.inText;
let stringDelim = '';
for (let i = 0; i < param.length; i++) {
const char = param[i];
switch (state) {
case ParseState.inText:
if (["'", '"', '`'].includes(char)) {
savePart();
stringDelim = char;
state = ParseState.inString;
}
else if (char == ' ') {
savePart();
}
else {
part += char;
}
break;
case ParseState.inString:
if (char === stringDelim) {
savePart();
state = ParseState.inText;
}
else {
part += char;
}
break;
}
}
// Add final part
savePart();
// Return renderer
return async (context, memory, functions, tokenizer, maxTokens) => {
const value = await functions.invokeFunction(name, context, memory, tokenizer, args);
return Utilities_1.Utilities.toString(tokenizer, value);
};
}
}
exports.TemplateSection = TemplateSection;
/**
* @private
*/
var ParseState;
(function (ParseState) {
ParseState[ParseState["inText"] = 0] = "inText";
ParseState[ParseState["inParameter"] = 1] = "inParameter";
ParseState[ParseState["inString"] = 2] = "inString";
})(ParseState || (ParseState = {}));
//# sourceMappingURL=TemplateSection.js.map