@microsoft/teams-ai
Version:
SDK focused on building AI based applications for Microsoft Teams.
143 lines • 6.34 kB
JavaScript
"use strict";
/**
* @module teams-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MonologueAugmentation = void 0;
const validators_1 = require("../validators");
const types_1 = require("../types");
const ActionAugmentationSection_1 = require("./ActionAugmentationSection");
/**
* @private
*/
const MISSING_ACTION_FEEDBACK = `The JSON returned had errors. Apply these fixes:\nadd the "action" property to "instance"`;
/**
* @private
*/
const SAY_REDIRECT_FEEDBACK = `The JSON returned was missing an action. Return a valid JSON object that contains your thoughts and uses the SAY action.`;
/**
* The 'monologue' augmentation.
* @remarks
* This augmentation adds support for an inner monologue to the prompt. The monologue helps the LLM
* to perform chain-of-thought reasoning across multiple turns of conversation.
*/
class MonologueAugmentation {
_section;
_monologueValidator = new validators_1.JSONResponseValidator(types_1.InnerMonologueSchema, `No valid JSON objects were found in the response. Return a valid JSON object with your thoughts and the next action to perform.`);
_actionValidator;
/**
* Creates a new `MonologueAugmentation` instance.
* @param {ChatCompletionAction[]} actions - List of actions supported by the prompt.
*/
constructor(actions) {
actions = appendSAYAction(actions);
this._section = new ActionAugmentationSection_1.ActionAugmentationSection(actions, [
`Return a JSON object with your thoughts and the next action to perform.`,
`Only respond with the JSON format below and base your plan on the actions above.`,
`If you're not sure what to do, you can always say something by returning a SAY action.`,
`If you're told your JSON response has errors, do your best to fix them.`,
`Response Format:`,
`{"thoughts":{"thought":"<your current thought>","reasoning":"<self reflect on why you made this decision>","plan":"- short bulleted\\n- list that conveys\\n- long-term plan"},"action":{"name":"<action name>","parameters":{"<name>":"<value>"}}}`
].join('\n'));
this._actionValidator = new validators_1.ActionResponseValidator(actions, true);
}
/**
* @returns {PromptSection|undefined} Returns an optional prompt section for the augmentation.
*/
createPromptSection() {
return this._section;
}
/**
* Validates a response to a prompt.
* @param {TurnContext} context - Context for the current turn of conversation with the user.
* @param {Memory} memory - An interface for accessing state values.
* @param {Tokenizer} tokenizer - Tokenizer to use for encoding and decoding text.
* @param {PromptResponse<string>} response - Response to validate.
* @param {number} remaining_attempts - Number of remaining attempts to validate the response.
* @returns {Validation} A `Validation` object.
*/
async validateResponse(context, memory, tokenizer, response, remaining_attempts) {
// Validate that we got a well-formed inner monologue
const validationResult = await this._monologueValidator.validateResponse(context, memory, tokenizer, response, remaining_attempts);
if (!validationResult.valid) {
// Catch the case where the action is missing.
// - GPT-3.5 gets stuck in a loop here sometimes so we'll redirect it to just use the SAY action.
if (validationResult.feedback == MISSING_ACTION_FEEDBACK) {
validationResult.feedback = SAY_REDIRECT_FEEDBACK;
}
return validationResult;
}
// Validate that the action exists and its parameters are valid
const monologue = validationResult.value;
const parameters = JSON.stringify(monologue.action.parameters ?? {});
const message = {
role: 'assistant',
content: undefined,
function_call: { name: monologue.action.name, arguments: parameters }
};
const actionValidation = await this._actionValidator.validateResponse(context, memory, tokenizer, { status: 'success', message }, remaining_attempts);
if (!actionValidation.valid) {
return actionValidation;
}
// Return the validated monologue
return validationResult;
}
/**
* Creates a plan given validated response value.
* @param {TurnContext} context - Context for the current turn of conversation.
* @param {Memory} memory - An interface for accessing state variables.
* @param {PromptResponse<InnerMonologue|undefined>} response - The validated and transformed response for the prompt.
* @returns {Plan} The created plan.
*/
createPlanFromResponse(context, memory, response) {
// Identify the action to perform
let command;
const monologue = response.message.content;
if (monologue.action.name == 'SAY') {
command = {
type: 'SAY',
response: {
...response.message,
content: monologue.action.parameters?.text || ''
}
};
}
else {
command = {
type: 'DO',
action: monologue.action.name,
parameters: monologue.action.parameters ?? {}
};
}
return Promise.resolve({ type: 'plan', commands: [command] });
}
}
exports.MonologueAugmentation = MonologueAugmentation;
/**
* @private
* @param {ChatCompletionAction[]} actions - List of actions
* @returns {ChatCompletionAction[]} The modified list of actions.
*/
function appendSAYAction(actions) {
const clone = actions.slice();
clone.push({
name: 'SAY',
description: 'use to ask the user a question or say something',
parameters: {
type: 'object',
properties: {
text: {
type: 'string',
description: 'text to say or question to ask'
}
},
required: ['text']
}
});
return clone;
}
//# sourceMappingURL=MonologueAugmentation.js.map