wingbot 3.69.7 → 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 +0 -2
- package/jsconfig.json +3 -3
- package/package.json +4 -3
- package/src/BuildRouter.js +71 -75
- package/src/ChatGpt.js +84 -34
- package/src/LLM.js +160 -0
- package/src/LLMMockProvider.js +67 -0
- package/src/LLMSession.js +228 -0
- package/src/Processor.js +28 -7
- package/src/Responder.js +175 -14
- package/src/ReturnSender.js +1 -0
- package/src/Tester.js +5 -0
- package/src/resolvers/button.js +1 -1
- package/src/resolvers/contextMessage.js +39 -0
- package/src/resolvers/include.js +3 -2
- package/src/resolvers/index.js +2 -0
- package/src/resolvers/message.js +45 -9
- package/src/resolvers/postback.js +5 -1
- package/src/resolvers/utils.js +1 -1
- package/src/utils/stateData.js +20 -10
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const LLM = require('./LLM');
|
|
7
|
+
|
|
8
|
+
/** @typedef {import('./Responder').QuickReply} QuickReply */
|
|
9
|
+
/** @typedef {'user'|'assistant'} LLMChatRole */
|
|
10
|
+
/** @typedef {'system'} LLMSystemRole */
|
|
11
|
+
/** @typedef {LLMChatRole|LLMSystemRole|string} LLMRole */
|
|
12
|
+
/** @typedef {import('./LLM').LLMProviderOptions} LLMProviderOptions */
|
|
13
|
+
|
|
14
|
+
/** @typedef {'stop'|'length'|'tool_calls'|'content_filter'} LLMFinishReason */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @template {LLMRole} [R=LLMRole]
|
|
18
|
+
* @typedef {object} LLMMessage
|
|
19
|
+
* @prop {R} role
|
|
20
|
+
* @prop {string} content
|
|
21
|
+
* @prop {LLMFinishReason} [finishReason]
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @callback SendCallback
|
|
26
|
+
* @param {LLMMessage[]} messages
|
|
27
|
+
* @param {QuickReply[]} quickReplies
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @class LLMSession
|
|
32
|
+
*/
|
|
33
|
+
class LLMSession {
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {LLM} llm
|
|
38
|
+
* @param {LLMMessage<any>[]} [chat]
|
|
39
|
+
* @param {SendCallback} [onSend]
|
|
40
|
+
*/
|
|
41
|
+
constructor (llm, chat = [], onSend = () => {}) {
|
|
42
|
+
this._llm = llm;
|
|
43
|
+
|
|
44
|
+
this._onSend = onSend;
|
|
45
|
+
|
|
46
|
+
/** @type {LLMMessage<any>[]} */
|
|
47
|
+
this._chat = chat;
|
|
48
|
+
|
|
49
|
+
this._generatedIndex = null;
|
|
50
|
+
|
|
51
|
+
this._sort();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_sort (what = this._chat) {
|
|
55
|
+
what.sort((a, z) => {
|
|
56
|
+
if (a.role === z.role
|
|
57
|
+
|| (a.role !== LLM.ROLE_SYSTEM && z.role !== LLM.ROLE_SYSTEM)) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
return a.role === LLM.ROLE_SYSTEM ? -1 : 1;
|
|
61
|
+
});
|
|
62
|
+
return what;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_mergeSystem () {
|
|
66
|
+
/** @type {LLMMessage<any>[]} */
|
|
67
|
+
const sysMessages = [];
|
|
68
|
+
|
|
69
|
+
const otherMessages = this._chat.filter((message) => {
|
|
70
|
+
if (message.role !== LLM.ROLE_SYSTEM) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
sysMessages.push(message);
|
|
74
|
+
return false;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (sysMessages.length === 0) {
|
|
78
|
+
return otherMessages;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const promptRegex = /\$\{prompt\(\)\}/g;
|
|
82
|
+
|
|
83
|
+
const content = sysMessages.reduce((reduced, current, i) => {
|
|
84
|
+
if (i === 0) {
|
|
85
|
+
return current.content || '';
|
|
86
|
+
}
|
|
87
|
+
if (!reduced.match(promptRegex)) {
|
|
88
|
+
return `${reduced}\n\n${current.content}`;
|
|
89
|
+
}
|
|
90
|
+
return reduced.replace(promptRegex, current.content).trim();
|
|
91
|
+
}, '')
|
|
92
|
+
.replace(promptRegex, '')
|
|
93
|
+
.trim();
|
|
94
|
+
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
role: LLM.ROLE_SYSTEM, content
|
|
98
|
+
},
|
|
99
|
+
...otherMessages
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @returns {LLMMessage[]}
|
|
105
|
+
*/
|
|
106
|
+
toArray () {
|
|
107
|
+
return this._mergeSystem();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @param {LLMMessage} msg
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
_msgPrefix (msg) {
|
|
116
|
+
switch (msg.role) {
|
|
117
|
+
case LLM.ROLE_SYSTEM:
|
|
118
|
+
return '-';
|
|
119
|
+
case LLM.ROLE_ASSISTANT:
|
|
120
|
+
return msg.content ? '<' : '#';
|
|
121
|
+
case LLM.ROLE_USER:
|
|
122
|
+
return '>';
|
|
123
|
+
default:
|
|
124
|
+
return '*';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
*
|
|
130
|
+
* @param {LLMMessage[]} [messages]
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
toString (messages = this._chat) {
|
|
134
|
+
if (messages.length === 0) {
|
|
135
|
+
return '[<empty>]';
|
|
136
|
+
}
|
|
137
|
+
return messages.map((m) => {
|
|
138
|
+
switch (m.role) {
|
|
139
|
+
case LLM.ROLE_SYSTEM:
|
|
140
|
+
return `--- system ---\n${m.content}\n--------------`;
|
|
141
|
+
default:
|
|
142
|
+
return `${this._msgPrefix(m)} ${m.content}`;
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
.join('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toJSON () {
|
|
149
|
+
return this.toArray();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
*
|
|
154
|
+
* @param {boolean} [needRaw=false]
|
|
155
|
+
* @returns {this}
|
|
156
|
+
*/
|
|
157
|
+
debug (needRaw = false) {
|
|
158
|
+
// eslint-disable-next-line no-console
|
|
159
|
+
console.log('LLMSession#debug\n', this.toString(
|
|
160
|
+
needRaw ? this._chat : this.toArray()
|
|
161
|
+
));
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
* @param {LLMMessage} message
|
|
168
|
+
* @returns {this}
|
|
169
|
+
*/
|
|
170
|
+
push (message) {
|
|
171
|
+
// if its system, append it on top
|
|
172
|
+
this._chat.push(message);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param {string} content
|
|
179
|
+
* @returns {this}
|
|
180
|
+
*/
|
|
181
|
+
systemPrompt (content) {
|
|
182
|
+
this.push({
|
|
183
|
+
role: LLM.ROLE_SYSTEM,
|
|
184
|
+
content
|
|
185
|
+
});
|
|
186
|
+
this._sort();
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
*
|
|
192
|
+
* @param {LLMProviderOptions} [options={}]
|
|
193
|
+
* @returns {Promise<LLMMessage<any>>}
|
|
194
|
+
*/
|
|
195
|
+
async generate (options = {}) {
|
|
196
|
+
const result = await this._llm.generate(this, options);
|
|
197
|
+
|
|
198
|
+
this._generatedIndex = this._chat.length;
|
|
199
|
+
this._chat.push(result);
|
|
200
|
+
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
*
|
|
206
|
+
* @param {QuickReply[]} quickReplies
|
|
207
|
+
* @returns {this}
|
|
208
|
+
*/
|
|
209
|
+
send (quickReplies = undefined) {
|
|
210
|
+
if (!this._generatedIndex) {
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
212
|
+
console.log('LLMSession', this.toString());
|
|
213
|
+
throw new Error('LLMSession: no message to send');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let messages = this._chat.splice(this._generatedIndex);
|
|
217
|
+
messages = messages.flatMap((msg) => LLM.toMessages(msg));
|
|
218
|
+
this._generatedIndex = null;
|
|
219
|
+
this._chat.push(...messages);
|
|
220
|
+
|
|
221
|
+
this._onSend(messages, quickReplies);
|
|
222
|
+
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = LLMSession;
|
package/src/Processor.js
CHANGED
|
@@ -11,6 +11,8 @@ const Request = require('./Request');
|
|
|
11
11
|
const Ai = require('./Ai');
|
|
12
12
|
const ReturnSender = require('./ReturnSender');
|
|
13
13
|
const { prepareState, mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
14
|
+
const LLM = require('./LLM');
|
|
15
|
+
const LLMMockProvider = require('./LLMMockProvider');
|
|
14
16
|
|
|
15
17
|
/** @typedef {import('./wingbot/CustomEntityDetectionModel').Intent} Intent */
|
|
16
18
|
/** @typedef {import('./ReducerWrapper')} ReducerWrapper */
|
|
@@ -19,6 +21,7 @@ const { prepareState, mergeState, isUserInteraction } = require('./utils/stateVa
|
|
|
19
21
|
/** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
|
|
20
22
|
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
21
23
|
/** @typedef {import('./analytics/consts').ResponseFlag} ResponseFlag */
|
|
24
|
+
/** @typedef {import('./LLM').LLMConfiguration} LLMConfiguration */
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* @typedef {object} AutoTypingConfig
|
|
@@ -110,6 +113,7 @@ const { prepareState, mergeState, isUserInteraction } = require('./utils/stateVa
|
|
|
110
113
|
* @prop {string} [apiUrl] - Url for calling orchestrator API
|
|
111
114
|
* @prop {Function} [fetch] - Fetch function for calling orchestrator API
|
|
112
115
|
* @prop {number} [sessionDuration] - Session duration for analytic purposes
|
|
116
|
+
* @prop {LLMConfiguration} [llm] - LLM model configuration
|
|
113
117
|
* @prop {Preloader<R>} [preloader]
|
|
114
118
|
*/
|
|
115
119
|
|
|
@@ -401,14 +405,19 @@ class Processor extends EventEmitter {
|
|
|
401
405
|
return { status: 304 };
|
|
402
406
|
}
|
|
403
407
|
|
|
408
|
+
const llm = new LLM({
|
|
409
|
+
provider: new LLMMockProvider(),
|
|
410
|
+
...this.options.llm
|
|
411
|
+
});
|
|
412
|
+
|
|
404
413
|
const result = await this
|
|
405
|
-
._dispatch(message, pageId, messageSender, responderData, preloadPromise);
|
|
414
|
+
._dispatch(message, pageId, messageSender, responderData, preloadPromise, llm);
|
|
406
415
|
|
|
407
416
|
messageSender.defer(preloadPromise, this.options.log);
|
|
408
417
|
return result;
|
|
409
418
|
}
|
|
410
419
|
|
|
411
|
-
async _dispatch (message, pageId, messageSender, responderData, preloadPromise) {
|
|
420
|
+
async _dispatch (message, pageId, messageSender, responderData, preloadPromise, llm) {
|
|
412
421
|
let req;
|
|
413
422
|
let res;
|
|
414
423
|
let state;
|
|
@@ -418,7 +427,14 @@ class Processor extends EventEmitter {
|
|
|
418
427
|
({
|
|
419
428
|
req, res, data, state
|
|
420
429
|
} = await this
|
|
421
|
-
._processMessage(
|
|
430
|
+
._processMessage(
|
|
431
|
+
message,
|
|
432
|
+
pageId,
|
|
433
|
+
messageSender,
|
|
434
|
+
responderData,
|
|
435
|
+
llm,
|
|
436
|
+
preloadPromise
|
|
437
|
+
));
|
|
422
438
|
|
|
423
439
|
messageSender.defer(this._emitInteractionEvent(req, res, messageSender, state, data));
|
|
424
440
|
|
|
@@ -548,6 +564,7 @@ class Processor extends EventEmitter {
|
|
|
548
564
|
pageId,
|
|
549
565
|
messageSender,
|
|
550
566
|
responderData,
|
|
567
|
+
llm,
|
|
551
568
|
preloadPromise = null,
|
|
552
569
|
senderMeta = null
|
|
553
570
|
) {
|
|
@@ -664,13 +681,13 @@ class Processor extends EventEmitter {
|
|
|
664
681
|
_sts: sessionTs,
|
|
665
682
|
_snew: sessionCreated
|
|
666
683
|
});
|
|
667
|
-
} else {
|
|
684
|
+
} /* else {
|
|
668
685
|
Object.assign(state, {
|
|
669
686
|
_snew: false
|
|
670
687
|
});
|
|
671
|
-
}
|
|
688
|
+
} */
|
|
672
689
|
|
|
673
|
-
prepareState(state, fromEvent, state._snew);
|
|
690
|
+
prepareState(state, fromEvent, state._snew && fromEvent);
|
|
674
691
|
|
|
675
692
|
const features = [
|
|
676
693
|
...(this.options.features || []),
|
|
@@ -690,7 +707,8 @@ class Processor extends EventEmitter {
|
|
|
690
707
|
options,
|
|
691
708
|
responderData,
|
|
692
709
|
configuration,
|
|
693
|
-
senderMeta
|
|
710
|
+
senderMeta,
|
|
711
|
+
llm
|
|
694
712
|
);
|
|
695
713
|
const postBack = this._createPostBack(postbackAcumulator, req, res, features);
|
|
696
714
|
|
|
@@ -852,6 +870,7 @@ class Processor extends EventEmitter {
|
|
|
852
870
|
pageId,
|
|
853
871
|
messageSender,
|
|
854
872
|
responderData,
|
|
873
|
+
llm,
|
|
855
874
|
res.senderMeta
|
|
856
875
|
);
|
|
857
876
|
|
|
@@ -949,6 +968,7 @@ class Processor extends EventEmitter {
|
|
|
949
968
|
pageId,
|
|
950
969
|
messageSender,
|
|
951
970
|
responderData,
|
|
971
|
+
llm,
|
|
952
972
|
senderMeta
|
|
953
973
|
) {
|
|
954
974
|
return postbackAcumulator.reduce((promise, postback) => promise
|
|
@@ -966,6 +986,7 @@ class Processor extends EventEmitter {
|
|
|
966
986
|
pageId,
|
|
967
987
|
messageSender,
|
|
968
988
|
responderData,
|
|
989
|
+
llm,
|
|
969
990
|
null,
|
|
970
991
|
senderMeta
|
|
971
992
|
);
|
package/src/Responder.js
CHANGED
|
@@ -17,6 +17,8 @@ const {
|
|
|
17
17
|
FEATURE_PHRASES
|
|
18
18
|
} = require('./features');
|
|
19
19
|
const transcriptFromHistory = require('./transcript/transcriptFromHistory');
|
|
20
|
+
const LLM = require('./LLM');
|
|
21
|
+
const LLMSession = require('./LLMSession');
|
|
20
22
|
|
|
21
23
|
const TYPE_RESPONSE = 'RESPONSE';
|
|
22
24
|
const TYPE_UPDATE = 'UPDATE';
|
|
@@ -28,8 +30,11 @@ const EXCEPTION_HOPCOUNT_THRESHOLD = 5;
|
|
|
28
30
|
/** @typedef {import('./ReturnSender').SendOptions} SendOptions */
|
|
29
31
|
/** @typedef {import('./ReturnSender').TextFilter} TextFilter */
|
|
30
32
|
/** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
|
|
31
|
-
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
32
33
|
/** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
|
|
34
|
+
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
35
|
+
|
|
36
|
+
/** @typedef {import('./LLM').LLMConfiguration} LLMConfiguration */
|
|
37
|
+
/** @typedef {import('./LLMSession').LLMMessage} LLMMessage */
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* @enum {string} ExpectedInput
|
|
@@ -98,6 +103,16 @@ Object.freeze(ExpectedInput);
|
|
|
98
103
|
* @prop {string} [name]
|
|
99
104
|
*/
|
|
100
105
|
|
|
106
|
+
/**
|
|
107
|
+
* @callback PromptGetter
|
|
108
|
+
* @param {Responder} res
|
|
109
|
+
* @returns {string|Promise<string>}
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @typedef {PromptGetter|string} PromptSource
|
|
114
|
+
*/
|
|
115
|
+
|
|
101
116
|
const PERSONA_DEFAULT = '_default';
|
|
102
117
|
|
|
103
118
|
/**
|
|
@@ -114,12 +129,14 @@ class Responder {
|
|
|
114
129
|
options = {},
|
|
115
130
|
data = {},
|
|
116
131
|
configuration = {},
|
|
117
|
-
senderMeta = null
|
|
132
|
+
senderMeta = null,
|
|
133
|
+
llm = null
|
|
118
134
|
) {
|
|
119
135
|
this._messageSender = messageSender;
|
|
120
136
|
this._senderId = senderId;
|
|
121
137
|
this._pageId = options.pageId;
|
|
122
138
|
this.token = token;
|
|
139
|
+
|
|
123
140
|
this._configuration = configuration;
|
|
124
141
|
|
|
125
142
|
/**
|
|
@@ -214,20 +231,156 @@ class Responder {
|
|
|
214
231
|
|
|
215
232
|
/** @type {SendOptions} */
|
|
216
233
|
this._nextMessageSendOptions = null;
|
|
234
|
+
|
|
235
|
+
/** @type {LLM} */
|
|
236
|
+
this.llm = llm;
|
|
237
|
+
|
|
238
|
+
this.LLM_CTX_DEFAULT = 'default';
|
|
239
|
+
|
|
240
|
+
/** @type {Map<string,(PromptSource|Promise<string>)[]>} */
|
|
241
|
+
this._llmContext = new Map([
|
|
242
|
+
[this.LLM_CTX_DEFAULT, []]
|
|
243
|
+
]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
*
|
|
248
|
+
* @param {PromptSource} systemPrompt
|
|
249
|
+
* @param {string} contextType
|
|
250
|
+
* @returns {this}
|
|
251
|
+
*/
|
|
252
|
+
llmAddSystemPrompt (systemPrompt, contextType = this.LLM_CTX_DEFAULT) {
|
|
253
|
+
if (!systemPrompt) {
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
if (!this._llmContext.has(contextType)) {
|
|
257
|
+
// @todo make it array of messages / maybe keep it in a single array
|
|
258
|
+
this._llmContext.set(contextType, []);
|
|
259
|
+
}
|
|
260
|
+
this._llmContext.get(contextType).push(systemPrompt);
|
|
261
|
+
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
|
|
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 }));
|
|
269
|
+
|
|
270
|
+
return new LLMSession(this.llm, chat, this._llmSend.bind(this));
|
|
271
|
+
}
|
|
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
|
+
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @param {string} contextType
|
|
286
|
+
* @param {string[]} [callStack]
|
|
287
|
+
* @returns {Promise<string[]>}
|
|
288
|
+
*/
|
|
289
|
+
async _getSystemContentForType (contextType, callStack = []) {
|
|
290
|
+
if (new Set(callStack).size < callStack.length) {
|
|
291
|
+
throw new Error(`Circular reference detected: contextType -> ${callStack}`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!this._llmContext.has(contextType)) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
|
|
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;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async llmSessionWithHistory (contextType = this.LLM_CTX_DEFAULT) {
|
|
323
|
+
const {
|
|
324
|
+
transcriptAnonymize,
|
|
325
|
+
transcriptFlag,
|
|
326
|
+
transcriptLength
|
|
327
|
+
} = this.llm.configuration;
|
|
328
|
+
|
|
329
|
+
const [systems, transcript] = await Promise.all([
|
|
330
|
+
this._getSystemContentForType(contextType),
|
|
331
|
+
this.getTranscript(transcriptLength, transcriptFlag)
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
const chat = [
|
|
335
|
+
...systems.map((content) => ({ role: LLM.ROLE_SYSTEM, content })),
|
|
336
|
+
...LLM.anonymizeTranscript(transcript, transcriptAnonymize)
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
return new LLMSession(this.llm, chat, this._llmSend.bind(this));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
*
|
|
344
|
+
* @param {LLMMessage[]} messages
|
|
345
|
+
* @param {QuickReply[]} quickReplies
|
|
346
|
+
*/
|
|
347
|
+
_llmSend (messages, quickReplies) {
|
|
348
|
+
this.setFlag(LLM.GPT_FLAG);
|
|
349
|
+
|
|
350
|
+
const { persona } = this.llm.configuration;
|
|
351
|
+
|
|
352
|
+
if (typeof persona === 'string') {
|
|
353
|
+
this.setPersona({ name: persona });
|
|
354
|
+
} else if (persona) {
|
|
355
|
+
this.setPersona(persona);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
messages.forEach((m, i) => {
|
|
359
|
+
const addQuickReply = i === (messages.length - 1);
|
|
360
|
+
this.text(m.content, addQuickReply ? quickReplies : null);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (persona) {
|
|
364
|
+
this.setPersona({ name: null });
|
|
365
|
+
}
|
|
217
366
|
}
|
|
218
367
|
|
|
219
368
|
_findPersonaConfiguration (name) {
|
|
369
|
+
// @ts-ignore
|
|
220
370
|
if (!name || !this._configuration.persona) {
|
|
221
371
|
return null;
|
|
222
372
|
}
|
|
373
|
+
// @ts-ignore
|
|
223
374
|
if (!this._configuration._cachedPersonas) {
|
|
224
|
-
//
|
|
375
|
+
// @ts-ignore
|
|
225
376
|
this._configuration._cachedPersonas = new Map(
|
|
377
|
+
// @ts-ignore
|
|
226
378
|
Object.entries(this._configuration.persona)
|
|
227
379
|
.map(([k, v]) => [k === PERSONA_DEFAULT ? k : tokenize(k), v])
|
|
228
380
|
);
|
|
229
381
|
}
|
|
230
382
|
const nameKey = name === PERSONA_DEFAULT ? PERSONA_DEFAULT : tokenize(name);
|
|
383
|
+
// @ts-ignore
|
|
231
384
|
return this._configuration._cachedPersonas.get(nameKey);
|
|
232
385
|
}
|
|
233
386
|
|
|
@@ -252,16 +405,16 @@ class Responder {
|
|
|
252
405
|
*/
|
|
253
406
|
async getTranscript (limit = 10, onlyFlag = null, skipThisTurnaround = false) {
|
|
254
407
|
const { chatLogStorage, timestamp } = this._messageSender;
|
|
255
|
-
|
|
256
|
-
|
|
408
|
+
let transcript = [];
|
|
409
|
+
if (chatLogStorage) {
|
|
410
|
+
transcript = await transcriptFromHistory(
|
|
411
|
+
chatLogStorage,
|
|
412
|
+
this._senderId,
|
|
413
|
+
this._pageId,
|
|
414
|
+
limit,
|
|
415
|
+
onlyFlag
|
|
416
|
+
);
|
|
257
417
|
}
|
|
258
|
-
const transcript = await transcriptFromHistory(
|
|
259
|
-
chatLogStorage,
|
|
260
|
-
this._senderId,
|
|
261
|
-
this._pageId,
|
|
262
|
-
limit,
|
|
263
|
-
onlyFlag
|
|
264
|
-
);
|
|
265
418
|
if (!skipThisTurnaround) {
|
|
266
419
|
const { responseTexts = [], requestTexts = [] } = this._messageSender;
|
|
267
420
|
transcript.push(...requestTexts.map((text) => ({
|
|
@@ -547,12 +700,18 @@ class Responder {
|
|
|
547
700
|
this.routePath = routePath;
|
|
548
701
|
}
|
|
549
702
|
|
|
703
|
+
/**
|
|
704
|
+
* @typedef {object} MessageOptions
|
|
705
|
+
* @prop {boolean} [disableAutoTyping]
|
|
706
|
+
*/
|
|
707
|
+
|
|
550
708
|
/**
|
|
551
709
|
* Send text as a response
|
|
552
710
|
*
|
|
553
711
|
* @param {string} text - text to send to user, can contain placeholders (%s)
|
|
554
712
|
* @param {Object.<string,string|QuickReply>|QuickReply[]} [replies] - quick replies
|
|
555
713
|
* @param {VoiceControl} [voice] - voice control data
|
|
714
|
+
* @param {MessageOptions} [options={}]
|
|
556
715
|
* @returns {this}
|
|
557
716
|
*
|
|
558
717
|
* @example
|
|
@@ -574,7 +733,7 @@ class Responder {
|
|
|
574
733
|
* }
|
|
575
734
|
* ]);
|
|
576
735
|
*/
|
|
577
|
-
text (text, replies = null, voice = null) {
|
|
736
|
+
text (text, replies = null, voice = null, options = {}) {
|
|
578
737
|
const messageData = {
|
|
579
738
|
message: {
|
|
580
739
|
text: this._t(text)
|
|
@@ -624,7 +783,9 @@ class Responder {
|
|
|
624
783
|
}
|
|
625
784
|
}
|
|
626
785
|
|
|
627
|
-
|
|
786
|
+
if (!options.disableAutoTyping) {
|
|
787
|
+
this._autoTypingIfEnabled(messageData.message.text);
|
|
788
|
+
}
|
|
628
789
|
this.send(messageData);
|
|
629
790
|
return this;
|
|
630
791
|
}
|
package/src/ReturnSender.js
CHANGED
|
@@ -11,6 +11,7 @@ const extractText = require('./transcript/extractText');
|
|
|
11
11
|
/** @typedef {import('./Request')} Request */
|
|
12
12
|
/** @typedef {import('./Responder')} Responder */
|
|
13
13
|
/** @typedef {import('./Processor').TrackingObject} TrackingObject */
|
|
14
|
+
/** @typedef {import('./LLMContext').LLMMessage} LLMMessage */
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @callback GetInteractions
|
package/src/Tester.js
CHANGED
|
@@ -18,6 +18,7 @@ const ResponseAssert = require('./testTools/ResponseAssert');
|
|
|
18
18
|
const Router = require('./Router'); // eslint-disable-line no-unused-vars
|
|
19
19
|
const ReducerWrapper = require('./ReducerWrapper'); // eslint-disable-line no-unused-vars
|
|
20
20
|
const { FEATURE_TEXT } = require('./features');
|
|
21
|
+
const LLMMockProvider = require('./LLMMockProvider');
|
|
21
22
|
|
|
22
23
|
/** @typedef {import('./Processor').ProcessorOptions<Router>} ProcessorOptions */
|
|
23
24
|
|
|
@@ -100,6 +101,10 @@ class Tester {
|
|
|
100
101
|
log,
|
|
101
102
|
// @ts-ignore
|
|
102
103
|
loadUsers: false,
|
|
104
|
+
llm: {
|
|
105
|
+
provider: new LLMMockProvider(),
|
|
106
|
+
...processorOptions.llm
|
|
107
|
+
},
|
|
103
108
|
...processorOptions
|
|
104
109
|
}));
|
|
105
110
|
|
package/src/resolvers/button.js
CHANGED
|
@@ -28,8 +28,8 @@ function button (params, context) {
|
|
|
28
28
|
buttons = [],
|
|
29
29
|
text = null
|
|
30
30
|
} = params;
|
|
31
|
-
const compiledText = cachedTranslatedCompilator(text);
|
|
32
31
|
|
|
32
|
+
const compiledText = cachedTranslatedCompilator(text);
|
|
33
33
|
const condition = getCondition(params, context, 'button');
|
|
34
34
|
|
|
35
35
|
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
@@ -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;
|