wingbot 3.53.5 → 3.55.0
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/package.json +1 -1
- package/plugins/ai.wingbot.openai/README.md +1 -0
- package/plugins/ai.wingbot.openai/plugin.js +119 -0
- package/plugins/plugins.json +80 -0
- package/src/Processor.js +10 -2
- package/src/Responder.js +47 -4
- package/src/ReturnSender.js +20 -2
- package/src/analytics/onInteractionHandler.js +7 -0
- package/src/transcript/transcriptFromHistory.js +3 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
> This plugin allows you to use chatGPT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author wingbot.ai
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const fetch = require('node-fetch').default;
|
|
7
|
+
const compileWithState = require('../../src/utils/compileWithState');
|
|
8
|
+
|
|
9
|
+
function chatgptPlugin (params) {
|
|
10
|
+
|
|
11
|
+
async function chatgpt (req, res) {
|
|
12
|
+
const content = req.text();
|
|
13
|
+
|
|
14
|
+
const token = compileWithState(req, res, params.token).trim();
|
|
15
|
+
|
|
16
|
+
// gpt-3.5-turbo-0301 gpt-3.5-turbo
|
|
17
|
+
const model = compileWithState(req, res, params.model) || 'gpt-3.5-turbo';
|
|
18
|
+
|
|
19
|
+
const system = compileWithState(req, res, params.system).trim();
|
|
20
|
+
|
|
21
|
+
// 0 - 2
|
|
22
|
+
const temperature = parseFloat(compileWithState(req, res, params.temperature).trim().replace(',', '.') || '1') || 1;
|
|
23
|
+
// presence_penalty between -2.0 and 2.0
|
|
24
|
+
const presence = parseFloat(compileWithState(req, res, params.presence).trim().replace(',', '.') || '0') || 0;
|
|
25
|
+
|
|
26
|
+
const maxTokens = parseFloat(compileWithState(req, res, params.maxTokens).trim() || '512') || 512;
|
|
27
|
+
|
|
28
|
+
const limit = parseInt(compileWithState(req, res, params.maxTokens).trim() || '10', 10) || 10;
|
|
29
|
+
|
|
30
|
+
const user = `${req.pageId}|${req.senderId}`;
|
|
31
|
+
|
|
32
|
+
const systemAfter = compileWithState(req, res, params.systemAfter).trim();
|
|
33
|
+
|
|
34
|
+
let body;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
res.typingOn();
|
|
38
|
+
|
|
39
|
+
body = {
|
|
40
|
+
model,
|
|
41
|
+
frequency_penalty: 0,
|
|
42
|
+
presence_penalty: presence,
|
|
43
|
+
max_tokens: maxTokens,
|
|
44
|
+
temperature,
|
|
45
|
+
user
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const ts = await res.getTranscript(limit);
|
|
49
|
+
|
|
50
|
+
const messages = [
|
|
51
|
+
...(system ? [{ role: 'system', content: system }] : []),
|
|
52
|
+
...ts.map((t) => ({ role: t.fromBot ? 'assistant' : 'user', content: t.text })),
|
|
53
|
+
{ role: 'user', content },
|
|
54
|
+
...(systemAfter ? [{ role: 'system', content: systemAfter }] : [])
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
Object.assign(body, { messages });
|
|
58
|
+
|
|
59
|
+
const useFetch = params.fetch || fetch;
|
|
60
|
+
|
|
61
|
+
const response = await useFetch('https://api.openai.com/v1/chat/completions', {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
Authorization: `Bearer ${token}`
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(body)
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
|
|
72
|
+
if (response.status !== 200
|
|
73
|
+
|| !Array.isArray(data.choices)) {
|
|
74
|
+
const { status, statusText } = response;
|
|
75
|
+
// eslint-disable-next-line no-console
|
|
76
|
+
console.log('chat gpt error', { status, statusText, data });
|
|
77
|
+
throw new Error(`Chat GPT ${status}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.log('chat gpt', JSON.stringify(data));
|
|
82
|
+
|
|
83
|
+
const sent = data.choices
|
|
84
|
+
.filter((ch) => ch.message && ch.message.role === 'assistant' && ch.message.content)
|
|
85
|
+
.map((ch) => {
|
|
86
|
+
let filtered = ch.message.content
|
|
87
|
+
.replace(/\n\n/g, '\n')
|
|
88
|
+
.split(/\n+(?!-)/g)
|
|
89
|
+
.filter((t) => !!t.trim());
|
|
90
|
+
|
|
91
|
+
if (filtered.length > 2) {
|
|
92
|
+
filtered = filtered.slice(0, filtered.length - 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
filtered
|
|
96
|
+
.forEach((t) => res.text(t.trim()));
|
|
97
|
+
|
|
98
|
+
return ch;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (sent.length === 0) {
|
|
102
|
+
const { status, statusText } = response;
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.log('chat gpt nothing to send', { status, statusText, data });
|
|
105
|
+
throw new Error('Chat GPT empty');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.error('chat gpt fail', e, body);
|
|
111
|
+
await res.run('fallback');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return chatgpt;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = chatgptPlugin;
|
package/plugins/plugins.json
CHANGED
|
@@ -484,6 +484,86 @@
|
|
|
484
484
|
],
|
|
485
485
|
"items": [
|
|
486
486
|
]
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
"id": "ai.wingbot.openai",
|
|
490
|
+
"name": "OpenAI - Chat",
|
|
491
|
+
"description": "Use within fallback to attach ChatGPT to your chatbot",
|
|
492
|
+
"availableSince": 3.54,
|
|
493
|
+
"editable": false,
|
|
494
|
+
"isFactory": true,
|
|
495
|
+
"category": "conversation",
|
|
496
|
+
"inputs": [
|
|
497
|
+
{
|
|
498
|
+
"name": "token",
|
|
499
|
+
"label": "API Key",
|
|
500
|
+
"type": "text",
|
|
501
|
+
"validations": [
|
|
502
|
+
{ "type": "regexp", "value": "^.+$", "message": "the token should be filled" }
|
|
503
|
+
]
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"type": "select",
|
|
507
|
+
"name": "model",
|
|
508
|
+
"label": "Chat GPT model",
|
|
509
|
+
"options": [
|
|
510
|
+
{ "value": "gpt-3.5-turbo", "label": "gpt-3.5-turbo" },
|
|
511
|
+
{ "value": "gpt-3.5-turbo-0301", "label": "gpt-3.5-turbo-0301" }
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"type": "textarea",
|
|
516
|
+
"name": "system",
|
|
517
|
+
"label": "Initial preset message (system)"
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
"type": "text",
|
|
521
|
+
"name": "temperature",
|
|
522
|
+
"label": "Temperature: 0.0 - 2.0 (1.0) - randomness of answers",
|
|
523
|
+
"validations": [
|
|
524
|
+
{
|
|
525
|
+
"type": "regexp",
|
|
526
|
+
"value": "^[0-9]*([.,][0-9]+)?$",
|
|
527
|
+
"message": "Temperature should be a valid float number"
|
|
528
|
+
}
|
|
529
|
+
]
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
"type": "text",
|
|
533
|
+
"name": "presence",
|
|
534
|
+
"label": "Presence: -2.0 - 2.0 (0.0) - likelihood to talk about new topics",
|
|
535
|
+
"validations": [
|
|
536
|
+
{
|
|
537
|
+
"type": "regexp",
|
|
538
|
+
"value": "^-?[0-9]*([.,][0-9]+)?$",
|
|
539
|
+
"message": "Presence should be a valid float number"
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
"type": "text",
|
|
545
|
+
"name": "maxTokens",
|
|
546
|
+
"label": "Max Tokens: 0 - 4096 (512) - max. number of returned tokens",
|
|
547
|
+
"validations": [
|
|
548
|
+
{
|
|
549
|
+
"type": "regexp",
|
|
550
|
+
"value": "^[0-9]*$",
|
|
551
|
+
"message": "Max tokens should be a valid number"
|
|
552
|
+
}
|
|
553
|
+
]
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"type": "textarea",
|
|
557
|
+
"name": "systemAfter",
|
|
558
|
+
"label": "Current message (system)"
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
"items": [
|
|
562
|
+
{
|
|
563
|
+
"id": "fallback",
|
|
564
|
+
"description": "If API call fails"
|
|
565
|
+
}
|
|
566
|
+
]
|
|
487
567
|
}
|
|
488
568
|
],
|
|
489
569
|
"categories": [
|
package/src/Processor.js
CHANGED
|
@@ -58,6 +58,8 @@ const { prepareState, mergeState, isUserInteraction } = require('./utils/stateVa
|
|
|
58
58
|
* @prop {object} state
|
|
59
59
|
* @prop {object} data
|
|
60
60
|
* @prop {string|null} skill
|
|
61
|
+
* @prop {string|null} prevSkill
|
|
62
|
+
* @prop {string|null} pathname
|
|
61
63
|
* @prop {TrackingObject} tracking - deprecated
|
|
62
64
|
* @prop {TrackingEvent[]} events
|
|
63
65
|
* @prop {ResponseFlag|null} flag
|
|
@@ -423,11 +425,14 @@ class Processor extends EventEmitter {
|
|
|
423
425
|
*/
|
|
424
426
|
_emitInteractionEvent (req, res, messageSender, state, data) {
|
|
425
427
|
const doNotTrack = data._initialEventShouldNotBeTracked === true;
|
|
426
|
-
const { _lastAction: lastAction = null } = req.state;
|
|
428
|
+
const { _lastAction: lastAction = null, '§pathname': pathname = null } = req.state;
|
|
427
429
|
const actions = messageSender.visitedInteractions;
|
|
428
430
|
const skill = typeof res.newState._trackAsSkill === 'undefined'
|
|
429
431
|
? (req.state._trackAsSkill || null)
|
|
430
432
|
: res.newState._trackAsSkill;
|
|
433
|
+
const prevSkill = typeof res.newState._trackPrevSkill === 'undefined'
|
|
434
|
+
? (req.state._trackPrevSkill || null)
|
|
435
|
+
: res.newState._trackPrevSkill;
|
|
431
436
|
const { events = [] } = messageSender.tracking;
|
|
432
437
|
|
|
433
438
|
const event = {
|
|
@@ -439,6 +444,8 @@ class Processor extends EventEmitter {
|
|
|
439
444
|
state,
|
|
440
445
|
data,
|
|
441
446
|
skill,
|
|
447
|
+
prevSkill,
|
|
448
|
+
pathname,
|
|
442
449
|
tracking: messageSender.tracking,
|
|
443
450
|
events,
|
|
444
451
|
flag: res.senderMeta.flag,
|
|
@@ -646,7 +653,8 @@ class Processor extends EventEmitter {
|
|
|
646
653
|
const options = {
|
|
647
654
|
...this.options,
|
|
648
655
|
state: Object.freeze({ ...state }),
|
|
649
|
-
features
|
|
656
|
+
features,
|
|
657
|
+
pageId
|
|
650
658
|
};
|
|
651
659
|
|
|
652
660
|
res = new Responder(senderId, messageSender, token, options, responderData);
|
package/src/Responder.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
FEATURE_SSML,
|
|
17
17
|
FEATURE_PHRASES
|
|
18
18
|
} = require('./features');
|
|
19
|
+
const transcriptFromHistory = require('./transcript/transcriptFromHistory');
|
|
19
20
|
|
|
20
21
|
const TYPE_RESPONSE = 'RESPONSE';
|
|
21
22
|
const TYPE_UPDATE = 'UPDATE';
|
|
@@ -26,6 +27,7 @@ const EXCEPTION_HOPCOUNT_THRESHOLD = 5;
|
|
|
26
27
|
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
27
28
|
/** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
|
|
28
29
|
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
30
|
+
/** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* @enum {string} ExpectedInput
|
|
@@ -89,6 +91,7 @@ class Responder {
|
|
|
89
91
|
constructor (senderId, messageSender, token = null, options = {}, data = {}) {
|
|
90
92
|
this._messageSender = messageSender;
|
|
91
93
|
this._senderId = senderId;
|
|
94
|
+
this._pageId = options.pageId;
|
|
92
95
|
this.token = token;
|
|
93
96
|
|
|
94
97
|
/**
|
|
@@ -178,6 +181,33 @@ class Responder {
|
|
|
178
181
|
this._recipient = { id: senderId };
|
|
179
182
|
|
|
180
183
|
this._textResponses = [];
|
|
184
|
+
|
|
185
|
+
this._typingSent = false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
*
|
|
190
|
+
* Returns current conversation transcript
|
|
191
|
+
*
|
|
192
|
+
* @param {number} [limit]
|
|
193
|
+
* @returns {Promise<Transcript[]>}
|
|
194
|
+
*/
|
|
195
|
+
async getTranscript (limit = 10) {
|
|
196
|
+
const { chatLogStorage } = this._messageSender;
|
|
197
|
+
if (!chatLogStorage) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
const transcript = await transcriptFromHistory(
|
|
201
|
+
chatLogStorage,
|
|
202
|
+
this._senderId,
|
|
203
|
+
this._pageId,
|
|
204
|
+
limit
|
|
205
|
+
);
|
|
206
|
+
const { responseTexts = [] } = chatLogStorage;
|
|
207
|
+
transcript.push(...responseTexts.map((text) => ({
|
|
208
|
+
fromBot: true, text
|
|
209
|
+
})));
|
|
210
|
+
return transcript;
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
/**
|
|
@@ -287,6 +317,7 @@ class Responder {
|
|
|
287
317
|
});
|
|
288
318
|
}
|
|
289
319
|
this.startedOutput = true;
|
|
320
|
+
this._typingSent = data.sender_action === 'typing_on';
|
|
290
321
|
this._messageSender.send(data);
|
|
291
322
|
return this;
|
|
292
323
|
}
|
|
@@ -979,10 +1010,11 @@ class Responder {
|
|
|
979
1010
|
/**
|
|
980
1011
|
* Sends "typing..." information
|
|
981
1012
|
*
|
|
1013
|
+
* @param {boolean} [force] - send even if was recently sent
|
|
982
1014
|
* @returns {this}
|
|
983
1015
|
*/
|
|
984
|
-
typingOn () {
|
|
985
|
-
return this._senderAction('typing_on');
|
|
1016
|
+
typingOn (force = false) {
|
|
1017
|
+
return this._senderAction('typing_on', force);
|
|
986
1018
|
}
|
|
987
1019
|
|
|
988
1020
|
/**
|
|
@@ -1230,7 +1262,15 @@ class Responder {
|
|
|
1230
1262
|
* @returns {this}
|
|
1231
1263
|
*/
|
|
1232
1264
|
trackAsSkill (skill) {
|
|
1233
|
-
|
|
1265
|
+
// @ts-ignore
|
|
1266
|
+
const { _trackAsSkill: currentSkill } = this.options.state;
|
|
1267
|
+
const setState = { _trackAsSkill: skill };
|
|
1268
|
+
if (currentSkill && currentSkill !== skill) {
|
|
1269
|
+
Object.assign(setState, {
|
|
1270
|
+
_trackPrevSkill: currentSkill
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
this.setState(setState);
|
|
1234
1274
|
return this;
|
|
1235
1275
|
}
|
|
1236
1276
|
|
|
@@ -1243,7 +1283,10 @@ class Responder {
|
|
|
1243
1283
|
return this._textResponses;
|
|
1244
1284
|
}
|
|
1245
1285
|
|
|
1246
|
-
_senderAction (action) {
|
|
1286
|
+
_senderAction (action, force = false) {
|
|
1287
|
+
if (action === 'typing_on' && this._typingSent && !force) {
|
|
1288
|
+
return this;
|
|
1289
|
+
}
|
|
1247
1290
|
const messageData = {
|
|
1248
1291
|
sender_action: action
|
|
1249
1292
|
};
|
package/src/ReturnSender.js
CHANGED
|
@@ -12,10 +12,21 @@ const extractText = require('./transcript/extractText');
|
|
|
12
12
|
/** @typedef {import('./Responder')} Responder */
|
|
13
13
|
/** @typedef {import('./Processor').TrackingObject} TrackingObject */
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @callback GetInteractions
|
|
17
|
+
* @param {string} senderId
|
|
18
|
+
* @param {string} pageId
|
|
19
|
+
* @param {number} [limit]
|
|
20
|
+
* @param {number} [endAt] - iterate backwards to history
|
|
21
|
+
* @param {number} [startAt] - iterate forward to last interaction
|
|
22
|
+
* @returns {Promise<object[]>}
|
|
23
|
+
*/
|
|
24
|
+
|
|
15
25
|
/**
|
|
16
26
|
* @typedef {object} ChatLogStorage
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
27
|
+
* @prop {Function} log
|
|
28
|
+
* @prop {Function} error
|
|
29
|
+
* @prop {GetInteractions} [getInteractions]
|
|
19
30
|
*/
|
|
20
31
|
|
|
21
32
|
/**
|
|
@@ -165,6 +176,13 @@ class ReturnSender {
|
|
|
165
176
|
.filter((t) => t && `${t}`.trim());
|
|
166
177
|
}
|
|
167
178
|
|
|
179
|
+
/**
|
|
180
|
+
* @returns {ChatLogStorage}
|
|
181
|
+
*/
|
|
182
|
+
get chatLogStorage () {
|
|
183
|
+
return this._logger;
|
|
184
|
+
}
|
|
185
|
+
|
|
168
186
|
_gotAnotherEvent () {
|
|
169
187
|
if (this._gotAnotherEventDefer) {
|
|
170
188
|
this._gotAnotherEventDefer();
|
|
@@ -40,6 +40,8 @@ const {
|
|
|
40
40
|
* @typedef {object} ConversationEventExtension
|
|
41
41
|
* @prop {string} [lastAction]
|
|
42
42
|
* @prop {string} [skill]
|
|
43
|
+
* @prop {string} [prevSkill]
|
|
44
|
+
* @prop {string} [pathname]
|
|
43
45
|
* @prop {string} [text]
|
|
44
46
|
* @prop {string} [expected]
|
|
45
47
|
* @prop {boolean} expectedTaken
|
|
@@ -268,6 +270,8 @@ function onInteractionHandler (
|
|
|
268
270
|
// state,
|
|
269
271
|
// data,
|
|
270
272
|
skill,
|
|
273
|
+
prevSkill,
|
|
274
|
+
pathname,
|
|
271
275
|
events,
|
|
272
276
|
flag,
|
|
273
277
|
nonInteractive,
|
|
@@ -371,6 +375,7 @@ function onInteractionHandler (
|
|
|
371
375
|
replaceDiacritics(req.text()).replace(/\s+/g, ' ').toLowerCase().trim()
|
|
372
376
|
);
|
|
373
377
|
const useSkill = (skill && webalize(skill)) || noneAction;
|
|
378
|
+
const usePrevSkill = (prevSkill && webalize(prevSkill)) || noneAction;
|
|
374
379
|
|
|
375
380
|
let winnerAction = '';
|
|
376
381
|
let winnerScore = 0;
|
|
@@ -425,6 +430,8 @@ function onInteractionHandler (
|
|
|
425
430
|
withUser,
|
|
426
431
|
feedback,
|
|
427
432
|
skill: useSkill,
|
|
433
|
+
prevSkill: usePrevSkill,
|
|
434
|
+
pathname: pathname || noneAction,
|
|
428
435
|
winnerAction,
|
|
429
436
|
winnerIntent,
|
|
430
437
|
winnerEntities: asArray(winnerEntities.map((e) => e.entity)),
|
|
@@ -33,6 +33,9 @@ const extractText = require('./extractText');
|
|
|
33
33
|
* @returns {Promise<Transcript[]>}
|
|
34
34
|
*/
|
|
35
35
|
async function transcriptFromHistory (chatLogStorage, senderId, pageId, limit = 20) {
|
|
36
|
+
if (typeof chatLogStorage.getInteractions !== 'function') {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
36
39
|
const data = await chatLogStorage.getInteractions(senderId, pageId, limit);
|
|
37
40
|
|
|
38
41
|
return data
|