wingbot 3.69.8 → 3.70.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -3,8 +3,6 @@
3
3
  */
4
4
  'use strict';
5
5
 
6
- /** @typedef {import('./src/Processor').ProcessorOptions<Router|BuildRouter>} ProcessorOptions */
7
-
8
6
  const Processor = require('./src/Processor');
9
7
  const Router = require('./src/Router');
10
8
  const Request = require('./src/Request');
package/jsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "module": "commonjs",
4
- "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"],
4
+ "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string", "dom"],
5
5
  "target": "ESNext",
6
6
  "allowSyntheticDefaultImports": true,
7
7
  "checkJs": true,
@@ -14,4 +14,4 @@
14
14
  "doc",
15
15
  "docs"
16
16
  ]
17
- }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.69.8",
3
+ "version": "3.70.0-alpha.4",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "homepage": "https://github.com/wingbot.ai/wingbot#readme",
39
39
  "devDependencies": {
40
+ "@types/mocha": "^10.0.10",
40
41
  "cpy-cli": "^5.0.0",
41
42
  "eslint": "^8.56.0",
42
43
  "eslint-config-airbnb": "^19.0.4",
@@ -69,4 +70,4 @@
69
70
  "axios": "^1.6.4",
70
71
  "handlebars": "^4.0.0"
71
72
  }
72
- }
73
+ }
@@ -571,8 +571,10 @@ class BuildRouter extends Router {
571
571
  };
572
572
 
573
573
  const [linksMap, nestedBlocksByStaticId] = this._createLinksMap(block);
574
+ // @ts-ignore
574
575
  this._linksMap = linksMap;
575
576
 
577
+ // @ts-ignore
576
578
  this._buildRoutes(block.routes, nestedBlocksByStaticId);
577
579
 
578
580
  this._configTs = setConfigTimestamp;
@@ -582,9 +584,10 @@ class BuildRouter extends Router {
582
584
  }
583
585
 
584
586
  /**
587
+ *
588
+ * returns {[LinksMap, BlockMap]}
585
589
  *
586
590
  * @param {Block} block
587
- * @returns {[LinksMap, BlockMap]}
588
591
  */
589
592
  _createLinksMap (block) {
590
593
  const { linksMap: prevLinksMap, blocks = [] } = this._resolvedContext;
package/src/LLM.js CHANGED
@@ -8,7 +8,7 @@ const { PHONE_REGEX, EMAIL_REGEX } = require('./systemEntities/regexps');
8
8
  /** @typedef {import('./Responder')} Responder */
9
9
  /** @typedef {import('./Responder').Persona} Persona */
10
10
  /** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
11
- /** @typedef {import('./LLMSession').LLMMessage} LLMMessage */
11
+ /** @typedef {import('./LLMSession').LLMMessage<any>} LLMMessage */
12
12
  /** @typedef {import('./LLMSession').LLMRole} LLMRole */
13
13
  /** @typedef {import('./LLMSession')} LLMSession */
14
14
  /** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
@@ -38,20 +38,27 @@ class LLMMockProvider {
38
38
  if (prompt.length === 0) {
39
39
  throw new Error('Empty prompt');
40
40
  }
41
- const stats = prompt.reduce((o, m) => Object.assign(o, {
42
- [m.role]: (o[m.role] || 0) + 1
43
- }), { system: 0, assistant: 0, user: 0 });
44
-
45
- const statsText = JSON.stringify(stats)
46
- .replace(/"/g, '');
47
-
48
- const message = this._sequence[this._index];
49
- this._index = (this._index + 1) % this._sequence.length;
41
+ // const stats = prompt.reduce((o, m) => Object.assign(o, {
42
+ // [m.role]: (o[m.role] || 0) + 1
43
+ // }), { system: 0, assistant: 0, user: 0 });
44
+ //
45
+ // const statsText = JSON.stringify(stats)
46
+ // .replace(/"/g, '');
47
+ //
48
+ /// / const message = this._sequence[this._index];
49
+ /// / this._index = (this._index + 1) % this._sequence.length;
50
+ //
51
+ // return {
52
+ // role: LLM.ROLE_ASSISTANT,
53
+ // finishReason: 'length',
54
+ // eslint-disable-next-line max-len
55
+ // content: `${statsText} > ${LLMMockProvider.DEFAULT_MODEL}: ${prompt.map((m) => m.content).join(' ')}`
56
+ // };
50
57
 
51
58
  return {
52
59
  role: LLM.ROLE_ASSISTANT,
53
60
  finishReason: 'length',
54
- content: `${statsText} > ${LLMMockProvider.DEFAULT_MODEL}: ${message}`
61
+ content: `${options.model || LLMMockProvider.DEFAULT_MODEL}:${prompt.map((m) => m.content).join(' ')}`
55
62
  };
56
63
  }
57
64
 
package/src/LLMSession.js CHANGED
@@ -35,7 +35,7 @@ class LLMSession {
35
35
  /**
36
36
  *
37
37
  * @param {LLM} llm
38
- * @param {LLMMessage[]} [chat]
38
+ * @param {LLMMessage<any>[]} [chat]
39
39
  * @param {SendCallback} [onSend]
40
40
  */
41
41
  constructor (llm, chat = [], onSend = () => {}) {
@@ -43,7 +43,7 @@ class LLMSession {
43
43
 
44
44
  this._onSend = onSend;
45
45
 
46
- /** @type {LLMMessage[]} */
46
+ /** @type {LLMMessage<any>[]} */
47
47
  this._chat = chat;
48
48
 
49
49
  this._generatedIndex = null;
@@ -63,6 +63,7 @@ class LLMSession {
63
63
  }
64
64
 
65
65
  _mergeSystem () {
66
+ /** @type {LLMMessage<any>[]} */
66
67
  const sysMessages = [];
67
68
 
68
69
  const otherMessages = this._chat.filter((message) => {
@@ -81,7 +82,7 @@ class LLMSession {
81
82
 
82
83
  const content = sysMessages.reduce((reduced, current, i) => {
83
84
  if (i === 0) {
84
- return current.content;
85
+ return current.content || '';
85
86
  }
86
87
  if (!reduced.match(promptRegex)) {
87
88
  return `${reduced}\n\n${current.content}`;
@@ -189,7 +190,7 @@ class LLMSession {
189
190
  /**
190
191
  *
191
192
  * @param {LLMProviderOptions} [options={}]
192
- * @returns {Promise<LLMMessage>}
193
+ * @returns {Promise<LLMMessage<any>>}
193
194
  */
194
195
  async generate (options = {}) {
195
196
  const result = await this._llm.generate(this, options);
package/src/Responder.js CHANGED
@@ -103,6 +103,16 @@ Object.freeze(ExpectedInput);
103
103
  * @prop {string} [name]
104
104
  */
105
105
 
106
+ /**
107
+ * @callback PromptGetter
108
+ * @param {Responder} res
109
+ * @returns {string|Promise<string>}
110
+ */
111
+
112
+ /**
113
+ * @typedef {PromptGetter|string} PromptSource
114
+ */
115
+
106
116
  const PERSONA_DEFAULT = '_default';
107
117
 
108
118
  /**
@@ -227,7 +237,7 @@ class Responder {
227
237
 
228
238
  this.LLM_CTX_DEFAULT = 'default';
229
239
 
230
- /** @type {Map<string,string[]>} */
240
+ /** @type {Map<string,(PromptSource|Promise<string>)[]>} */
231
241
  this._llmContext = new Map([
232
242
  [this.LLM_CTX_DEFAULT, []]
233
243
  ]);
@@ -235,36 +245,48 @@ class Responder {
235
245
 
236
246
  /**
237
247
  *
238
- * @param {string} systemPrompt
248
+ * @param {PromptSource} systemPrompt
239
249
  * @param {string} contextType
240
250
  * @returns {this}
241
251
  */
242
252
  llmAddSystemPrompt (systemPrompt, contextType = this.LLM_CTX_DEFAULT) {
243
- if (!systemPrompt || !systemPrompt.trim()) {
253
+ if (!systemPrompt) {
244
254
  return this;
245
255
  }
246
256
  if (!this._llmContext.has(contextType)) {
247
257
  // @todo make it array of messages / maybe keep it in a single array
248
258
  this._llmContext.set(contextType, []);
249
259
  }
250
- this._llmContext.get(contextType).push(systemPrompt.trim());
260
+ this._llmContext.get(contextType).push(systemPrompt);
261
+
251
262
  return this;
252
263
  }
253
264
 
254
- llmSession (contextType = this.LLM_CTX_DEFAULT) {
255
- const chat = this._getSystemContentForType(contextType)
256
- .map((content) => ({ role: LLM.ROLE_SYSTEM, content }));
265
+ async llmSession (contextType = this.LLM_CTX_DEFAULT) {
266
+ const system = await this._getSystemContentForType(contextType);
267
+
268
+ const chat = system.map((content) => ({ role: LLM.ROLE_SYSTEM, content }));
257
269
 
258
270
  return new LLMSession(this.llm, chat, this._llmSend.bind(this));
259
271
  }
260
272
 
273
+ async _replaceAsync (str, regex, asyncFn) {
274
+ const promises = [];
275
+ str.replace(regex, (full, ...args) => {
276
+ promises.push(asyncFn(full, ...args));
277
+ return full;
278
+ });
279
+ const data = await Promise.all(promises);
280
+ return str.replace(regex, () => data.shift());
281
+ }
282
+
261
283
  /**
262
284
  *
263
285
  * @param {string} contextType
264
286
  * @param {string[]} [callStack]
265
- * @returns {string[]}
287
+ * @returns {Promise<string[]>}
266
288
  */
267
- _getSystemContentForType (contextType, callStack = []) {
289
+ async _getSystemContentForType (contextType, callStack = []) {
268
290
  if (new Set(callStack).size < callStack.length) {
269
291
  throw new Error(`Circular reference detected: contextType -> ${callStack}`);
270
292
  }
@@ -273,10 +295,28 @@ class Responder {
273
295
  return [];
274
296
  }
275
297
 
276
- return this._llmContext.get(contextType)
277
- .map((c) => c.replace(/\$\{([a-zA-Z0-9\s]+)\}/g, (str, requestType) => this
278
- ._getSystemContentForType(requestType, [...callStack, contextType])
279
- .join('\n\n')).trim());
298
+ /** @type {Promise<string>[]} */
299
+ const promiseStrings = this._llmContext.get(contextType)
300
+ .map(async (p) => (typeof p === 'function' ? p(this) : p));
301
+ this._llmContext.set(contextType, promiseStrings);
302
+
303
+ const resolved = await Promise.all(
304
+ promiseStrings
305
+ .map(async (promiseString) => {
306
+ const s = await promiseString;
307
+
308
+ const replaced = await this._replaceAsync(s.trim(), /\$\{([a-zA-Z0-9\s]+)\}/g, async (str, reqType) => {
309
+ const nested = await this
310
+ ._getSystemContentForType(reqType, [...callStack, contextType]);
311
+
312
+ return nested.join('\n\n');
313
+ });
314
+
315
+ return replaced.trim();
316
+ })
317
+ );
318
+
319
+ return resolved;
280
320
  }
281
321
 
282
322
  async llmSessionWithHistory (contextType = this.LLM_CTX_DEFAULT) {
@@ -286,8 +326,10 @@ class Responder {
286
326
  transcriptLength
287
327
  } = this.llm.configuration;
288
328
 
289
- const systems = this._getSystemContentForType(contextType);
290
- const transcript = await this.getTranscript(transcriptLength, transcriptFlag);
329
+ const [systems, transcript] = await Promise.all([
330
+ this._getSystemContentForType(contextType),
331
+ this.getTranscript(transcriptLength, transcriptFlag)
332
+ ]);
291
333
 
292
334
  const chat = [
293
335
  ...systems.map((content) => ({ role: LLM.ROLE_SYSTEM, content })),
@@ -658,12 +700,18 @@ class Responder {
658
700
  this.routePath = routePath;
659
701
  }
660
702
 
703
+ /**
704
+ * @typedef {object} MessageOptions
705
+ * @prop {boolean} [disableAutoTyping]
706
+ */
707
+
661
708
  /**
662
709
  * Send text as a response
663
710
  *
664
711
  * @param {string} text - text to send to user, can contain placeholders (%s)
665
712
  * @param {Object.<string,string|QuickReply>|QuickReply[]} [replies] - quick replies
666
713
  * @param {VoiceControl} [voice] - voice control data
714
+ * @param {MessageOptions} [options={}]
667
715
  * @returns {this}
668
716
  *
669
717
  * @example
@@ -685,7 +733,7 @@ class Responder {
685
733
  * }
686
734
  * ]);
687
735
  */
688
- text (text, replies = null, voice = null) {
736
+ text (text, replies = null, voice = null, options = {}) {
689
737
  const messageData = {
690
738
  message: {
691
739
  text: this._t(text)
@@ -735,7 +783,9 @@ class Responder {
735
783
  }
736
784
  }
737
785
 
738
- this._autoTypingIfEnabled(messageData.message.text);
786
+ if (!options.disableAutoTyping) {
787
+ this._autoTypingIfEnabled(messageData.message.text);
788
+ }
739
789
  this.send(messageData);
740
790
  return this;
741
791
  }
@@ -0,0 +1,39 @@
1
+ /*
2
+ * @author Vojtech Jedlicka
3
+ */
4
+ 'use strict';
5
+
6
+ const Router = require('../Router');
7
+ // eslint-disable-next-line no-unused-vars
8
+ const Responder = require('../Responder');
9
+ const { getLanguageText } = require('./utils');
10
+ const compileWithState = require('../utils/compileWithState');
11
+
12
+ /** @typedef {import('../BuildRouter').BotContext<any>} BotContext */
13
+ /** @typedef {import('../Router').Resolver<any>} Resolver */
14
+ /** @typedef {import('./utils').Translations} Translations */
15
+
16
+ /**
17
+ *
18
+ * @param {object} params
19
+ * @param {Translations} params.context
20
+ * @param {string} [params.type]
21
+ * @param {BotContext} context
22
+ * @returns {Resolver}
23
+ */
24
+ // eslint-disable-next-line no-unused-vars
25
+ function contextMessage (params, context) {
26
+
27
+ return async (req, res) => {
28
+ res.llmAddSystemPrompt((r) => {
29
+ const translated = getLanguageText(params.context, req.state.lang);
30
+ const translatedText = Array.isArray(translated) ? translated[0] : translated;
31
+ const statefulPrompt = compileWithState(req, r, translatedText);
32
+ return statefulPrompt;
33
+ }, params.type);
34
+
35
+ return Router.CONTINUE;
36
+ };
37
+ }
38
+
39
+ module.exports = contextMessage;
@@ -17,10 +17,12 @@ const subscribtions = require('./subscribtions');
17
17
  const setState = require('./setState');
18
18
  const expectedInput = require('./expectedInput');
19
19
  const skip = require('./skip');
20
+ const contextMessage = require('./contextMessage');
20
21
 
21
22
  module.exports = {
22
23
  path,
23
24
  message,
25
+ contextMessage,
24
26
  include,
25
27
  postback,
26
28
  expected,
@@ -270,12 +270,23 @@ function selectTranslation (resolverId, params, texts, data) {
270
270
  ];
271
271
  }
272
272
 
273
- /** @typedef {import('../BuildRouter').BotContext} BotContext */
274
- /** @typedef {import('../Router').Resolver} Resolver */
273
+ /** @typedef {import('../BuildRouter').BotContext<any>} BotContext */
274
+ /** @typedef {import('../Router').Resolver<any>} Resolver */
275
275
 
276
276
  /**
277
277
  *
278
278
  * @param {object} params
279
+ * @param {any} params.text
280
+ * @param {boolean} [params.hasCondition]
281
+ * @param {string} [params.conditionFn]
282
+ * @param {string} [params.conditionDesc]
283
+ * @param {boolean} [params.hasEditableCondition]
284
+ * @param {any} [params.editableCondition]
285
+ * @param {string} [params.mode]
286
+ * @param {string} [params.persist]
287
+ * @param {any[]} [params.replies]
288
+ * @param {"message" | "prompt"} [params.type]
289
+ * @param {string} [params.llmContextType]
279
290
  * @param {BotContext} context
280
291
  * @returns {Resolver}
281
292
  */
@@ -284,6 +295,7 @@ function message (params, context = {}) {
284
295
  // @ts-ignore
285
296
  isLastIndex, isLastMessage, linksMap, configuration, resolverId
286
297
  } = context;
298
+
287
299
  if (typeof params.text !== 'string' && !Array.isArray(params.text)) {
288
300
  throw new Error('Message should be a text!');
289
301
  }
@@ -294,7 +306,6 @@ function message (params, context = {}) {
294
306
  throw new Error('Replies should be an array');
295
307
  }
296
308
 
297
- // compile condition
298
309
  let condition;
299
310
 
300
311
  const ret = isLastIndex ? Router.END : Router.CONTINUE;
@@ -303,10 +314,11 @@ function message (params, context = {}) {
303
314
  * @param {Request} req
304
315
  * @param {Responder} res
305
316
  */
306
- return (req, res) => {
317
+ return async (req, res) => {
307
318
  if (condition === undefined) {
308
319
  condition = getCondition(params, context, 'Message condition');
309
320
  }
321
+
310
322
  if (quickReplies === undefined) {
311
323
  if (params.replies && params.replies.length > 0) {
312
324
  quickReplies = parseReplies(params.replies, linksMap, context);
@@ -324,7 +336,8 @@ function message (params, context = {}) {
324
336
  data.lang
325
337
  );
326
338
 
327
- const [text, seqState] = selectTranslation(
339
+ // eslint-disable-next-line prefer-const
340
+ let [text, seqState] = selectTranslation(
328
341
  resolverId,
329
342
  params,
330
343
  supportedText.translations,
@@ -403,6 +416,20 @@ function message (params, context = {}) {
403
416
  };
404
417
  }
405
418
 
419
+ if (params.type === 'prompt') {
420
+ const session = await res.llmSessionWithHistory(params.llmContextType);
421
+
422
+ const response = await session.systemPrompt(text)
423
+ .debug()
424
+ .generate();
425
+
426
+ // if (!response.content) {
427
+ // // no response?
428
+ // }
429
+
430
+ text = response.content;
431
+ }
432
+
406
433
  res.text(text, sendReplies, voiceControl);
407
434
 
408
435
  if (isLastMessage && !req.actionData()._resolverTag) {
@@ -62,7 +62,7 @@ function isTextObjectEmpty (text) {
62
62
  * @param {Translations} translations
63
63
  * @param {string} [lang]
64
64
  * @param {boolean} [disableDefaulting] - it will try to find translation for other language
65
- * @returns {null|string}
65
+ * @returns {null|string|string[]}
66
66
  */
67
67
  function getLanguageText (translations, lang = null, disableDefaulting = false) {
68
68
  let foundText;