wingbot 3.53.5 → 3.54.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 +2 -1
- package/src/Responder.js +38 -3
- package/src/ReturnSender.js +20 -2
- 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
|
@@ -646,7 +646,8 @@ class Processor extends EventEmitter {
|
|
|
646
646
|
const options = {
|
|
647
647
|
...this.options,
|
|
648
648
|
state: Object.freeze({ ...state }),
|
|
649
|
-
features
|
|
649
|
+
features,
|
|
650
|
+
pageId
|
|
650
651
|
};
|
|
651
652
|
|
|
652
653
|
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
|
/**
|
|
@@ -1243,7 +1275,10 @@ class Responder {
|
|
|
1243
1275
|
return this._textResponses;
|
|
1244
1276
|
}
|
|
1245
1277
|
|
|
1246
|
-
_senderAction (action) {
|
|
1278
|
+
_senderAction (action, force = false) {
|
|
1279
|
+
if (action === 'typing_on' && this._typingSent && !force) {
|
|
1280
|
+
return this;
|
|
1281
|
+
}
|
|
1247
1282
|
const messageData = {
|
|
1248
1283
|
sender_action: action
|
|
1249
1284
|
};
|
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();
|
|
@@ -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
|