wingbot
Version:
Enterprise Messaging Bot Conversation Engine
161 lines (133 loc) • 4.02 kB
JavaScript
/**
* @author David Menger
*/
;
const { PHONE_REGEX, EMAIL_REGEX } = require('./systemEntities/regexps');
/** @typedef {import('./Responder')} Responder */
/** @typedef {import('./Responder').Persona} Persona */
/** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
/** @typedef {import('./LLMSession').LLMMessage} LLMMessage */
/** @typedef {import('./LLMSession').LLMRole} LLMRole */
/** @typedef {import('./LLMSession')} LLMSession */
/** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
/**
* @callback LLMChatProviderPrompt
* @param {LLMMessage[]} prompt
* @param {LLMProviderOptions} [options]
* @returns {Promise<LLMMessage>}
*/
/**
* @typedef {object} LLMProviderOptions
* @prop {string} [model]
*/
/**
* @typedef {object} LLMChatProvider
* @prop {LLMChatProviderPrompt} requestChat
*/
/** @typedef {import('node-fetch').default} Fetch */
/**
* @typedef {object} LLMConfiguration
* @prop {LLMChatProvider} provider
* @prop {string} [model]
* @prop {number} [transcriptLength=-5]
* @prop {'gpt'|string} [transcriptFlag]
* @prop {boolean} [transcriptAnonymize]
* @prop {Persona|string|null} [persona]
*/
/**
* @typedef {object} AnonymizeRegexp
* @prop {string} [replacement]
* @prop {RegExp} regex
*/
/**
* @class LLM
*/
class LLM {
/** @type {LLMRole} */
static ROLE_USER = 'user';
/** @type {LLMRole} */
static ROLE_ASSISTANT = 'assistant';
/** @type {LLMRole} */
static ROLE_SYSTEM = 'system';
static GPT_FLAG = 'gpt';
/** @type {AnonymizeRegexp[]} */
static anonymizeRegexps = [
{ replacement: '@PHONE', regex: new RegExp(PHONE_REGEX.source, 'g') },
{ replacement: '@EMAIL', regex: new RegExp(EMAIL_REGEX.source, 'g') }
];
/**
*
* @param {LLMConfiguration} configuration
*/
constructor (configuration) {
const { provider, ...rest } = configuration;
this._configuration = {
transcriptFlag: 'gpt',
transcriptLength: 5,
provider: null,
...rest
};
/** @type {LLMChatProvider} */
this._provider = provider;
}
/**
* @returns {Omit<LLMConfiguration, 'provider'>}
*/
get configuration () {
return this._configuration;
}
/**
*
* @param {Transcript[]} chat
* @param {boolean} [transcriptAnonymize]
* @returns {LLMMessage[]}
*/
static anonymizeTranscript (chat, transcriptAnonymize) {
return chat.map((c) => ({
role: c.fromBot ? LLM.ROLE_ASSISTANT : LLM.ROLE_USER,
content: transcriptAnonymize
? LLM.anonymizeRegexps
.reduce((text, { replacement, regex }) => {
const replaced = text.replace(regex, replacement);
return replaced;
}, c.text)
: c.text
}));
}
/**
*
* @param {LLMSession} session
* @param {LLMProviderOptions} [options={}]
* @returns {Promise<LLMMessage>}
*/
async generate (session, options = {}) {
/** @type {LLMProviderOptions} */
const opts = {
model: this._configuration.model,
...options
};
const prompt = session.toArray();
const result = await this._provider.requestChat(prompt, opts);
return result;
}
/**
*
* @param {LLMMessage} result
* @returns {LLMMessage[]}
*/
static toMessages (result) {
let filtered = result.content
.replace(/\n\n+/g, '\n')
.split(/\n+(?!-)/g)
.map((t) => t.trim())
.filter((t) => !!t);
if (result.finishReason === 'length' && filtered.length <= 0) {
filtered = filtered.slice(0, filtered.length - 1);
}
return filtered.map((content) => ({
content,
role: result.role
}));
}
}
module.exports = LLM;