wingbot 3.74.8 → 3.75.9-alpha.2
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/.claude/settings.local.json +3 -1
- package/index.js +6 -1
- package/jsconfig.json +2 -1
- package/package.json +1 -1
- package/src/BuildRouter.js +43 -7
- package/src/GlobalIntents.js +198 -0
- package/src/LLM.js +19 -3
- package/src/LLMDispatcher.js +508 -0
- package/src/LLMMockProvider.js +44 -1
- package/src/LLMRouter.js +325 -0
- package/src/LLMSession.js +87 -21
- package/src/LLMType.js +13 -13
- package/src/Processor.js +18 -14
- package/src/ReducerWrapper.js +24 -0
- package/src/Request.js +88 -231
- package/src/Responder.js +28 -8
- package/src/Router.js +21 -26
- package/src/Tester.js +43 -2
- package/src/prompt.js +138 -0
- package/src/resolvers/bounce.js +4 -2
- package/src/resolvers/expected.js +4 -3
- package/src/tools/routeToEvents.js +1 -0
- package/src/wingbot/CustomEntityDetectionModel.js +1 -1
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const LLMRouter = require('./LLMRouter');
|
|
7
|
+
const { parseActionPayload } = require('./utils');
|
|
8
|
+
const { quickReplyAction } = require('./utils/quickReplies');
|
|
9
|
+
const stateData = require('./utils/stateData');
|
|
10
|
+
const { checkSetState } = require('./utils/stateVariables');
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('./Request')} Request */
|
|
13
|
+
/** @typedef {import('./Responder')} Responder */
|
|
14
|
+
/** @typedef {import('./GlobalIntents').GlobalIntentsMap} GlobalIntentsMap */
|
|
15
|
+
/** @typedef {import('./GlobalIntents').ResolvedGlobalIntentsMap} ResolvedGlobalIntentsMap */
|
|
16
|
+
/** @typedef {import('./GlobalIntents').GlobalIntent} GlobalIntent */
|
|
17
|
+
/** @typedef {import('./LLMRouter').RouterResolver} RouterResolver */
|
|
18
|
+
/** @typedef {import('./Ai')} Ai */
|
|
19
|
+
/** @typedef {import('./LLM')} LLM */
|
|
20
|
+
/** @typedef {import('./LLMRouter').LLMRouting} LLMRouting */
|
|
21
|
+
/** @typedef {import('./LLMRouter').RoutingAction} RoutingAction */
|
|
22
|
+
/** @typedef {import('./utils/quickReplies').QuickReplyAction} QuickReplyAction */
|
|
23
|
+
/** @typedef {import('./LLMRouter').LLMRoutingCache} LLMRoutingCache */
|
|
24
|
+
|
|
25
|
+
const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} DispatcherRouting
|
|
29
|
+
* @prop {Map<string, GlobalIntent>} giByAction
|
|
30
|
+
* @prop {GlobalIntent[]} nlpMatchers
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {LLMRouting & DispatcherRouting} Routing
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {object} ILLMRouter
|
|
39
|
+
* @prop {RouterResolver} resolve
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {object} Logger
|
|
44
|
+
* @prop {Function} log
|
|
45
|
+
* @prop {Function} error
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {object} DispatcherOptions
|
|
50
|
+
* @prop {Logger} [log]
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @class LLMDispatcher
|
|
55
|
+
*/
|
|
56
|
+
class LLMDispatcher {
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
* @param {Ai} ai
|
|
61
|
+
* @param {Routing} routing
|
|
62
|
+
* @param {LLM} [llm]
|
|
63
|
+
* @param {DispatcherOptions} options
|
|
64
|
+
*/
|
|
65
|
+
constructor (ai, routing = null, llm = null, options = {}) {
|
|
66
|
+
this._ai = ai;
|
|
67
|
+
this._llm = llm;
|
|
68
|
+
|
|
69
|
+
/** @type {Routing} */
|
|
70
|
+
this._routing = routing || {
|
|
71
|
+
router: null,
|
|
72
|
+
nlpMatchers: [],
|
|
73
|
+
giByAction: new Map(),
|
|
74
|
+
globals: {
|
|
75
|
+
actionListString: null,
|
|
76
|
+
actionList: [],
|
|
77
|
+
router: null,
|
|
78
|
+
byAction: new Map()
|
|
79
|
+
},
|
|
80
|
+
locals: new Map()
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** @type {QuickReplyAction[]} */
|
|
84
|
+
this._quickReplyActions = null;
|
|
85
|
+
|
|
86
|
+
/** @type {RoutingAction} */
|
|
87
|
+
this._action = null;
|
|
88
|
+
|
|
89
|
+
/** @type {RoutingAction[]} */
|
|
90
|
+
this._aiActions = null;
|
|
91
|
+
|
|
92
|
+
/** @type {Request} */
|
|
93
|
+
this._req = null;
|
|
94
|
+
|
|
95
|
+
this._log = options.log || console;
|
|
96
|
+
|
|
97
|
+
this._dispatched = false;
|
|
98
|
+
|
|
99
|
+
this._overrideAction = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
*
|
|
104
|
+
* @param {RoutingAction} action
|
|
105
|
+
*/
|
|
106
|
+
overrideAction (action) {
|
|
107
|
+
this._overrideAction = action;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @param {Request} req
|
|
113
|
+
* @param {Responder} [res]
|
|
114
|
+
* @returns {Promise<RoutingAction>}
|
|
115
|
+
*/
|
|
116
|
+
async dispatch (req, res) {
|
|
117
|
+
this._req = req;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
this._action = await this._postbackAction(res);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// console.log('DISPATCH FAILED -----\n\n', e);
|
|
123
|
+
this._log.error('LLMDispatch failed', e);
|
|
124
|
+
throw e;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// console.log('DISPATCHed -----\n\n');
|
|
128
|
+
this._dispatched = true;
|
|
129
|
+
return this._action;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param {Responder} [responder]
|
|
135
|
+
* @returns {Promise<RoutingAction>}
|
|
136
|
+
*/
|
|
137
|
+
async _postbackAction (responder = null) {
|
|
138
|
+
let res;
|
|
139
|
+
if (this._req.referral !== null && this._req.referral.ref) {
|
|
140
|
+
res = parseActionPayload({ payload: this._req.referral.ref });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!res && this._req.postback !== null) {
|
|
144
|
+
res = parseActionPayload(this._req.postback);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!res && this._req.optin !== null && this._req.optin.ref) {
|
|
148
|
+
res = this._base64Ref(this._req.optin);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!res && this._req.optin !== null && this._req.optin.payload) {
|
|
152
|
+
res = parseActionPayload(this._req.optin);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!res && this._req.message !== null && this._req.message.quick_reply) {
|
|
156
|
+
res = parseActionPayload(this._req.message.quick_reply);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* TADY MUSI BYT HOTOVE AI
|
|
161
|
+
* - jsou nasazeny handlery s AI?
|
|
162
|
+
* - nepustíme už rovnou LLM?
|
|
163
|
+
* - AI vždy-s-textem/jen-s-handlery/
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
await this._ai.preloadAi(this._req, responder);
|
|
167
|
+
|
|
168
|
+
if (!res && this._req.state._expectedKeywords) {
|
|
169
|
+
res = this._actionByExpectedKeywords(this._req.state._expected);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let bounceAllow = false;
|
|
173
|
+
if (!res && this._req.state._expected) {
|
|
174
|
+
res = parseActionPayload(this._req.state._expected);
|
|
175
|
+
bounceAllow = !!(res
|
|
176
|
+
&& this._req.state._expected.bounce === 'allow');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this._aiActions = this._resolveAiActions();
|
|
180
|
+
|
|
181
|
+
const routing = this._localLLmRoutingForState(this._req.state);
|
|
182
|
+
|
|
183
|
+
let aiRes;
|
|
184
|
+
let llmRes;
|
|
185
|
+
if ((!res || bounceAllow) && routing?.router) {
|
|
186
|
+
aiRes = await routing.router.resolve(this._llm, this._req, routing);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if ((!res || bounceAllow) && !aiRes) {
|
|
190
|
+
llmRes = this._getAiWinnerResAfterResolved();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (aiRes || llmRes) {
|
|
194
|
+
res = aiRes || llmRes;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!res
|
|
198
|
+
&& routing.keepUserInInteractionsWithBounceAllowed
|
|
199
|
+
&& this._req.state.lastAction
|
|
200
|
+
&& !this._req.state.lastAction.match(/\*$/)) {
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
action: this._req.state.lastAction
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return this._applySetStateToDetectedAction(res);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_getAiWinnerResAfterResolved () {
|
|
211
|
+
const [winner] = this._aiActions;
|
|
212
|
+
|
|
213
|
+
if (!winner?.winner) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const _aiKeys = winner.setState ? Object.keys(winner.setState) : [];
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
action: winner.action, data: {}, setState: winner.setState, _aiKeys
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
*
|
|
226
|
+
* @param {*} state
|
|
227
|
+
* @returns {LLMRoutingCache}
|
|
228
|
+
*/
|
|
229
|
+
_localLLmRoutingForState (state) {
|
|
230
|
+
const { lastAction } = state;
|
|
231
|
+
|
|
232
|
+
/** @type {LLMRoutingCache} */
|
|
233
|
+
let routing = this._routing.globals;
|
|
234
|
+
|
|
235
|
+
if (lastAction) {
|
|
236
|
+
let normalized = lastAction.replace(/\/$/, '');
|
|
237
|
+
|
|
238
|
+
if (this._routing.locals.has(normalized)) {
|
|
239
|
+
routing = this._routing.locals.get(normalized);
|
|
240
|
+
} else {
|
|
241
|
+
normalized = normalized.replace(/\/[^/]+$/, '');
|
|
242
|
+
if (this._routing.locals.has(normalized)) {
|
|
243
|
+
routing = this._routing.locals.get(normalized);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const router = routing?.router || this._routing.router;
|
|
249
|
+
|
|
250
|
+
if (!routing) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
...routing,
|
|
256
|
+
router
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
*
|
|
262
|
+
* - dokonce i na "expectedKeywords"
|
|
263
|
+
*
|
|
264
|
+
* @param {RoutingAction} res
|
|
265
|
+
* @returns {RoutingAction}
|
|
266
|
+
*/
|
|
267
|
+
_applySetStateToDetectedAction (res) {
|
|
268
|
+
// find global intent
|
|
269
|
+
if (!res) {
|
|
270
|
+
return res;
|
|
271
|
+
}
|
|
272
|
+
let { setState = {} } = res;
|
|
273
|
+
let entitiesSetState = {};
|
|
274
|
+
const gi = this._routing.giByAction.get(res.action);
|
|
275
|
+
if (gi && gi.action === res.action) {
|
|
276
|
+
entitiesSetState = { ...gi.entitiesSetState };
|
|
277
|
+
|
|
278
|
+
const values = Array.from(Object.values(entitiesSetState));
|
|
279
|
+
|
|
280
|
+
for (const value of values) {
|
|
281
|
+
if (typeof value === 'function') {
|
|
282
|
+
Object.assign(entitiesSetState, value(stateData(this._req)));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const newState = {
|
|
287
|
+
...entitiesSetState,
|
|
288
|
+
...setState
|
|
289
|
+
};
|
|
290
|
+
checkSetState(setState, newState);
|
|
291
|
+
setState = newState;
|
|
292
|
+
const aiKeysSet = new Set([
|
|
293
|
+
...(res._aiKeys || []),
|
|
294
|
+
...Object.keys(entitiesSetState)
|
|
295
|
+
]);
|
|
296
|
+
const aiKeys = Array.from(aiKeysSet)
|
|
297
|
+
.filter((k) => typeof setState[k] !== 'undefined' && k.startsWith('@'));
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
...res,
|
|
301
|
+
setState,
|
|
302
|
+
_aiKeys: aiKeys
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Returns resolved action
|
|
308
|
+
*
|
|
309
|
+
* @returns {RoutingAction}
|
|
310
|
+
*/
|
|
311
|
+
action () {
|
|
312
|
+
if (this._overrideAction) {
|
|
313
|
+
return this._overrideAction;
|
|
314
|
+
}
|
|
315
|
+
// if (!this._dispatched) {
|
|
316
|
+
// throw new Error('LLM dispatcher wansn\'t dispatched!');
|
|
317
|
+
// }
|
|
318
|
+
return this._action;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Returns full detected AI actions
|
|
323
|
+
*
|
|
324
|
+
* @returns {RoutingAction[]}
|
|
325
|
+
*/
|
|
326
|
+
aiActions () {
|
|
327
|
+
if (!this._dispatched) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
return this._aiActions;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Returns full detected AI action
|
|
335
|
+
*
|
|
336
|
+
* @returns {RoutingAction}
|
|
337
|
+
*/
|
|
338
|
+
aiActionWinner () {
|
|
339
|
+
// if (!this._dispatched) {
|
|
340
|
+
// throw new Error('LLM dispatcher wansn\'t dispatched!');
|
|
341
|
+
// }
|
|
342
|
+
return this._aiActions[0]?.winner ? this._aiActions[0] : null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Yields global intents at the current request path.
|
|
347
|
+
*
|
|
348
|
+
* @returns {Generator<[GlobalIntent, boolean]>}
|
|
349
|
+
*/
|
|
350
|
+
* globalIntentsAtPath () {
|
|
351
|
+
// to match the local context intent
|
|
352
|
+
const localRegexToMatch = this._req.getLocalPathRegexp();
|
|
353
|
+
|
|
354
|
+
for (const gi of this._routing.nlpMatchers) {
|
|
355
|
+
const pathMatches = localRegexToMatch && localRegexToMatch.exec(gi.action);
|
|
356
|
+
if (gi.local && !pathMatches) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
yield [gi, !!pathMatches];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Returns full detected AI action
|
|
365
|
+
*
|
|
366
|
+
* @returns {RoutingAction[]}
|
|
367
|
+
*/
|
|
368
|
+
_resolveAiActions () {
|
|
369
|
+
if (!this._req.isTextOrIntent()) {
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @typedef {object} ExtendRoutingAction
|
|
375
|
+
* @prop {number} sort
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
/** @type {(RoutingAction & ExtendRoutingAction)[]} */
|
|
379
|
+
const aiActions = [];
|
|
380
|
+
|
|
381
|
+
const iterator = this.globalIntentsAtPath();
|
|
382
|
+
|
|
383
|
+
for (const [gi, pathMatches] of iterator) {
|
|
384
|
+
const intent = gi.matcher(this._req); // , null, true
|
|
385
|
+
if (intent !== null) {
|
|
386
|
+
const sort = intent.score + (pathMatches
|
|
387
|
+
? this._ai.localEnhancement
|
|
388
|
+
: 0);
|
|
389
|
+
// console.log(sort, wi.intent);
|
|
390
|
+
aiActions.push({
|
|
391
|
+
action: gi.action,
|
|
392
|
+
...gi,
|
|
393
|
+
intent,
|
|
394
|
+
setState: intent.setState,
|
|
395
|
+
aboveConfidence: intent.aboveConfidence,
|
|
396
|
+
sort,
|
|
397
|
+
winner: false
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
aiActions.sort((l, r) => r.sort - l.sort);
|
|
403
|
+
this._setWinner(aiActions);
|
|
404
|
+
return aiActions;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_setWinner (aiActions) {
|
|
408
|
+
if (aiActions.length === 0 || !aiActions[0].aboveConfidence) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// there will be no winner, if there are two different intents
|
|
413
|
+
if (this._ai.shouldDisambiguate(aiActions)) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (aiActions[0]) {
|
|
418
|
+
// eslint-disable-next-line no-param-reassign
|
|
419
|
+
aiActions[0].winner = true;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return aiActions[0];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
_actionByExpectedKeywords (expected) {
|
|
426
|
+
// @ts-ignore
|
|
427
|
+
if (!this._req.state._expectedKeywords) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const actions = this.resolveQuickReplyActions();
|
|
432
|
+
|
|
433
|
+
// @ts-ignore
|
|
434
|
+
if (expected && this._ai.shouldDisambiguate(actions, true)) {
|
|
435
|
+
return parseActionPayload(expected);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const [payload] = actions;
|
|
439
|
+
if (!payload || !payload.aboveConfidence) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return parseActionPayload(payload);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
*
|
|
448
|
+
* @returns {QuickReplyAction[]}
|
|
449
|
+
*/
|
|
450
|
+
resolveQuickReplyActions () {
|
|
451
|
+
if (this._quickReplyActions === null) {
|
|
452
|
+
if (this._req.state._expectedKeywords) {
|
|
453
|
+
this._quickReplyActions = quickReplyAction(
|
|
454
|
+
this._req.state._expectedKeywords,
|
|
455
|
+
this._req,
|
|
456
|
+
this._ai
|
|
457
|
+
);
|
|
458
|
+
} else {
|
|
459
|
+
this._quickReplyActions = [];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return this._quickReplyActions;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
_base64Ref (object = {}) {
|
|
466
|
+
let process = {};
|
|
467
|
+
|
|
468
|
+
if (object && object.ref) {
|
|
469
|
+
let payload = object.ref;
|
|
470
|
+
|
|
471
|
+
if (typeof payload === 'string' && payload.match(BASE64_REGEX)) {
|
|
472
|
+
payload = Buffer.from(payload, 'base64').toString('utf8');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
process = { payload };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return parseActionPayload(process);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
*
|
|
483
|
+
* @param {ResolvedGlobalIntentsMap} globalIntents
|
|
484
|
+
* @returns {Routing}
|
|
485
|
+
*/
|
|
486
|
+
static prepareRouting (globalIntents) {
|
|
487
|
+
/** @type {Map<string,GlobalIntent>} */
|
|
488
|
+
const giByAction = new Map();
|
|
489
|
+
const giMatchersByAction = new Map();
|
|
490
|
+
for (const gi of globalIntents.values()) {
|
|
491
|
+
if (gi.action) {
|
|
492
|
+
giByAction.set(gi.action, gi);
|
|
493
|
+
if (gi.matcher) {
|
|
494
|
+
giMatchersByAction.set(gi.action, gi);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
...LLMRouter.prepareRouting(globalIntents),
|
|
501
|
+
nlpMatchers: Array.from(giMatchersByAction.values()),
|
|
502
|
+
giByAction
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
module.exports = LLMDispatcher;
|
package/src/LLMMockProvider.js
CHANGED
|
@@ -8,6 +8,18 @@ const LLM = require('./LLM');
|
|
|
8
8
|
/** @typedef {import('./LLM').LLMChatProvider} LLMChatProvider */
|
|
9
9
|
/** @typedef {import('./LLM').LLMMessage} LLMMessage */
|
|
10
10
|
/** @typedef {import('./LLM').LLMProviderOptions} LLMProviderOptions */
|
|
11
|
+
/** @typedef {import('./LLM').LLMPresetName} LLMPresetName */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @template {object|string} [C=object|string]
|
|
15
|
+
* @typedef {object} LLMMockResponse
|
|
16
|
+
* @prop {LLMPresetName} preset
|
|
17
|
+
* @prop {C} content
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {LLMMockResponse<{action:string}>} LLMRoutingResponse
|
|
22
|
+
*/
|
|
11
23
|
|
|
12
24
|
/**
|
|
13
25
|
* @class LLMMockProvider
|
|
@@ -17,7 +29,12 @@ class LLMMockProvider {
|
|
|
17
29
|
|
|
18
30
|
static DEFAULT_MODEL = 'mockmodel';
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param {LLMMockResponse[]} [mocks]
|
|
35
|
+
*/
|
|
36
|
+
constructor (mocks = []) {
|
|
37
|
+
this._mocks = mocks || [];
|
|
21
38
|
this._index = 0;
|
|
22
39
|
this._sequence = [
|
|
23
40
|
'lorem',
|
|
@@ -28,6 +45,26 @@ class LLMMockProvider {
|
|
|
28
45
|
];
|
|
29
46
|
}
|
|
30
47
|
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {string} preset
|
|
51
|
+
* @returns {LLMMessage}
|
|
52
|
+
*/
|
|
53
|
+
_mockResponseForPreset (preset) {
|
|
54
|
+
const found = this._mocks.find((mock) => mock.preset === preset);
|
|
55
|
+
|
|
56
|
+
if (!found) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
role: LLM.ROLE_ASSISTANT,
|
|
62
|
+
content: typeof found.content === 'object'
|
|
63
|
+
? JSON.stringify(found.content)
|
|
64
|
+
: found.content
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
31
68
|
/**
|
|
32
69
|
* @param {LLMMessage[]} prompt
|
|
33
70
|
* @param {LLMProviderOptions} [options]
|
|
@@ -65,6 +102,12 @@ class LLMMockProvider {
|
|
|
65
102
|
};
|
|
66
103
|
}
|
|
67
104
|
|
|
105
|
+
// simple for now
|
|
106
|
+
const mockResponse = this._mockResponseForPreset(options.preset || 'default');
|
|
107
|
+
if (mockResponse) {
|
|
108
|
+
return mockResponse;
|
|
109
|
+
}
|
|
110
|
+
|
|
68
111
|
// const stats = prompt.reduce((o, m) => Object.assign(o, {
|
|
69
112
|
// [m.role]: (o[m.role] || 0) + 1
|
|
70
113
|
// }), { system: 0, assistant: 0, user: 0 });
|