wingbot 3.74.8 → 3.75.9-alpha.1
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 +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 +85 -14
- 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
- package/.claude/settings.local.json +0 -8
package/src/LLMRouter.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const GlobalIntents = require('./GlobalIntents');
|
|
7
|
+
const LLMType = require('./LLMType');
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('./GlobalIntents').GlobalIntentsMap} GlobalIntentsMap */
|
|
10
|
+
/** @typedef {import('./GlobalIntents').ResolvedGlobalIntentsMap} ResolvedGlobalIntentsMap */
|
|
11
|
+
/** @typedef {import('./GlobalIntents').GlobalIntentResolved} GlobalIntentResolved */
|
|
12
|
+
/** @typedef {import('./GlobalIntents').MatchingResolver} MatchingResolver */
|
|
13
|
+
/** @typedef {import('./GlobalIntents').MatcherResult} MatcherResult */
|
|
14
|
+
/** @typedef {import('./LLMDispatcher').ILLMRouter} ILLMRouter */
|
|
15
|
+
/** @typedef {import('./Request')} Request */
|
|
16
|
+
/** @typedef {import('./LLM')} LLM */
|
|
17
|
+
/** @typedef {import('./prompt').Prompt} Prompt */
|
|
18
|
+
/** @typedef {import('./Router').RoutingInstruction} RoutingInstruction */
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {object} NLPActionInfo
|
|
22
|
+
* @prop {MatcherResult} intent
|
|
23
|
+
* @prop {boolean} aboveConfidence
|
|
24
|
+
* @prop {boolean} winner
|
|
25
|
+
* @prop {string} [title]
|
|
26
|
+
* @prop {string|string[]} [match]
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} BaseRouteAction
|
|
31
|
+
* @prop {string} action
|
|
32
|
+
* @prop {object} [data]
|
|
33
|
+
* @prop {object} [setState]
|
|
34
|
+
* @prop {object} [_aiKeys]
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {object} LLMRouting
|
|
39
|
+
* @prop {LLMRoutingCache} globals
|
|
40
|
+
* @prop {ILLMRouter|null} router
|
|
41
|
+
* @prop {Map<string, LLMRoutingCache>} locals
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {Pick<GlobalIntentResolved, 'action'|'classification'>} LLMAction
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {BaseRouteAction & Partial<NLPActionInfo>} RoutingAction
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @callback RouterResolver
|
|
54
|
+
* @param {LLM} llm
|
|
55
|
+
* @param {Request} req
|
|
56
|
+
* @param {LLMRoutingInput} routing
|
|
57
|
+
*
|
|
58
|
+
* @returns {Promise<RoutingAction|null>}
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @typedef {Pick<LLMRoutingCache, 'actionList'|'actionListString'|'byAction'>} LLMRoutingInput
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @typedef {object} LLMRoutingCache
|
|
67
|
+
* @prop {boolean} [keepUserInInteractionsWithBounceAllowed]
|
|
68
|
+
* @prop {string} actionListString
|
|
69
|
+
* @prop {LLMAction[]} actionList
|
|
70
|
+
* @prop {ILLMRouter|null} router
|
|
71
|
+
* @prop {Map<string, GlobalIntentResolved>} byAction
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {object} LLMRouterOptions
|
|
76
|
+
* @prop {boolean} [keepUserInInteractionsWithBounceAllowed]
|
|
77
|
+
* @prop {boolean} [global]
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {Omit<LLMRouterOptions, 'global'>} GILLMExtension
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @class LLMRouter
|
|
86
|
+
*/
|
|
87
|
+
class LLMRouter {
|
|
88
|
+
|
|
89
|
+
// lets give it "reduce" method? - can forward that info to dispatcher
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
*
|
|
93
|
+
* @param {RouterResolver} routeResolver
|
|
94
|
+
* @param {Omit<LLMRouterOptions, 'global'>} [options]
|
|
95
|
+
*/
|
|
96
|
+
static global (routeResolver, options = {}) {
|
|
97
|
+
return new LLMRouter(routeResolver, {
|
|
98
|
+
...options,
|
|
99
|
+
global: true
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
*
|
|
105
|
+
* @param {RouterResolver} [routeResolver]
|
|
106
|
+
* @param {LLMRouterOptions} [options]
|
|
107
|
+
*/
|
|
108
|
+
constructor (routeResolver = null, options = {}) {
|
|
109
|
+
const { global = false, ...rest } = options;
|
|
110
|
+
this._route = routeResolver;
|
|
111
|
+
this._global = global;
|
|
112
|
+
|
|
113
|
+
/** @typedef {GlobalIntentsMap} */
|
|
114
|
+
this.globalIntents = new Map();
|
|
115
|
+
|
|
116
|
+
if (global) {
|
|
117
|
+
const gi = GlobalIntents.create({
|
|
118
|
+
router: this._route ? this : null,
|
|
119
|
+
...rest
|
|
120
|
+
});
|
|
121
|
+
this.globalIntents.set(gi.id, gi);
|
|
122
|
+
} else if (Object.keys(rest).length) {
|
|
123
|
+
const gi = GlobalIntents.create({
|
|
124
|
+
local: true,
|
|
125
|
+
...rest
|
|
126
|
+
});
|
|
127
|
+
this.globalIntents.set(gi.id, gi);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @type {object}
|
|
133
|
+
*/
|
|
134
|
+
static _cachedStructuredOutput = null;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
*
|
|
138
|
+
* @returns {object}
|
|
139
|
+
*/
|
|
140
|
+
static structuredOutput () {
|
|
141
|
+
if (LLMRouter._cachedStructuredOutput === null) {
|
|
142
|
+
LLMRouter._cachedStructuredOutput = LLMType
|
|
143
|
+
.object({
|
|
144
|
+
action: LLMType.string()
|
|
145
|
+
.description('recommended action')
|
|
146
|
+
}, 'conversation routing information')
|
|
147
|
+
.toJSON();
|
|
148
|
+
}
|
|
149
|
+
return LLMRouter._cachedStructuredOutput;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
*
|
|
154
|
+
* prompt wi
|
|
155
|
+
*
|
|
156
|
+
* @param {Prompt} prompt
|
|
157
|
+
* @returns {RouterResolver}
|
|
158
|
+
*/
|
|
159
|
+
static defaultRoute (prompt) {
|
|
160
|
+
/** @type {RouterResolver} */
|
|
161
|
+
const route = async (llm, req, routing) => {
|
|
162
|
+
const res = await llm.session('routing')
|
|
163
|
+
.setData(routing)
|
|
164
|
+
.systemPrompt(prompt)
|
|
165
|
+
.generateStructured(LLMRouter.structuredOutput());
|
|
166
|
+
|
|
167
|
+
if (!routing.byAction.has(LLMRouter._normByAction(res.action))) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return res;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return route;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
*
|
|
179
|
+
* @returns {RoutingInstruction}
|
|
180
|
+
*/
|
|
181
|
+
reduce () {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
on () {
|
|
186
|
+
// acually does nothing too
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
*
|
|
191
|
+
* @param {LLM} llm
|
|
192
|
+
* @param {Request} req
|
|
193
|
+
* @param {LLMRoutingInput} routing
|
|
194
|
+
* @returns {Promise<RoutingAction|null>}
|
|
195
|
+
*/
|
|
196
|
+
async resolve (llm, req, routing) { // eslint-disable-line no-unused-vars
|
|
197
|
+
if (!this._route) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return this._route(llm, req, routing);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
*
|
|
205
|
+
* @param {string} path
|
|
206
|
+
* @param {string} classification
|
|
207
|
+
* @returns {MatchingResolver}
|
|
208
|
+
*/
|
|
209
|
+
route (path, classification) {
|
|
210
|
+
return GlobalIntents.resolver(path, {
|
|
211
|
+
classification,
|
|
212
|
+
router: this._route ? this : null,
|
|
213
|
+
local: !this._global
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
*
|
|
219
|
+
* @param {string} path
|
|
220
|
+
* @param {string} classification
|
|
221
|
+
* @returns {MatchingResolver}
|
|
222
|
+
*/
|
|
223
|
+
static route (path, classification) {
|
|
224
|
+
return GlobalIntents.resolver(path, {
|
|
225
|
+
classification
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static _normByAction (act) {
|
|
230
|
+
return act.replace(/^\/|\/$/g, '');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*
|
|
235
|
+
* @param {GlobalIntentResolved[]} resolvedLlm
|
|
236
|
+
* @returns {LLMRoutingCache}
|
|
237
|
+
*/
|
|
238
|
+
static _prepareRoutingInput (resolvedLlm) {
|
|
239
|
+
const byAction = new Map();
|
|
240
|
+
let router = null;
|
|
241
|
+
let keepUserInInteractionsWithBounceAllowed;
|
|
242
|
+
|
|
243
|
+
const actionList = resolvedLlm.map((gi) => {
|
|
244
|
+
const { action, classification } = gi;
|
|
245
|
+
byAction.set(LLMRouter._normByAction(gi.action), gi);
|
|
246
|
+
|
|
247
|
+
if (typeof gi.keepUserInInteractionsWithBounceAllowed === 'boolean') {
|
|
248
|
+
keepUserInInteractionsWithBounceAllowed = true;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (router && router !== gi.router) {
|
|
252
|
+
throw new Error(`Multiple local routers on "${gi.localPath}"`);
|
|
253
|
+
} else {
|
|
254
|
+
router = gi.router;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { action, classification };
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const actionListString = actionList
|
|
261
|
+
.map((gi) => `- \`${gi.action}\`: ${gi.classification}`)
|
|
262
|
+
.join('\n');
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
...(typeof keepUserInInteractionsWithBounceAllowed === 'boolean'
|
|
266
|
+
? { keepUserInInteractionsWithBounceAllowed }
|
|
267
|
+
: {}),
|
|
268
|
+
actionListString,
|
|
269
|
+
actionList,
|
|
270
|
+
byAction,
|
|
271
|
+
router
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
*
|
|
277
|
+
* @param {ResolvedGlobalIntentsMap} globalIntents
|
|
278
|
+
* @returns {LLMRouting}
|
|
279
|
+
*/
|
|
280
|
+
static prepareRouting (globalIntents) {
|
|
281
|
+
/** @type {Map<string, GlobalIntentResolved[]>} */
|
|
282
|
+
const locals = new Map();
|
|
283
|
+
/** @type {GlobalIntentResolved[]} */
|
|
284
|
+
const globals = [];
|
|
285
|
+
/** @type {ILLMRouter} */
|
|
286
|
+
let router = null;
|
|
287
|
+
|
|
288
|
+
for (const gi of globalIntents.values()) {
|
|
289
|
+
if (!gi.classification
|
|
290
|
+
&& !gi.router
|
|
291
|
+
&& typeof gi.keepUserInInteractionsWithBounceAllowed === 'undefined') continue;
|
|
292
|
+
|
|
293
|
+
if (gi.local) {
|
|
294
|
+
let local = locals.get(gi.localPath);
|
|
295
|
+
if (!local) {
|
|
296
|
+
local = [];
|
|
297
|
+
locals.set(gi.localPath, local);
|
|
298
|
+
}
|
|
299
|
+
local.push(gi);
|
|
300
|
+
} else if (gi.classification) {
|
|
301
|
+
globals.push(gi);
|
|
302
|
+
} else if (gi.router) { // public default router without classification
|
|
303
|
+
if (router !== null) {
|
|
304
|
+
throw new Error(`Detected more than one global router on '${gi.localPath}'. Only single global router allowed.`);
|
|
305
|
+
}
|
|
306
|
+
router = gi.router;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
router,
|
|
312
|
+
locals: new Map(
|
|
313
|
+
Array.from(locals)
|
|
314
|
+
.map(([localPath, locs]) => [
|
|
315
|
+
localPath,
|
|
316
|
+
LLMRouter._prepareRoutingInput(locs)
|
|
317
|
+
])
|
|
318
|
+
),
|
|
319
|
+
globals: LLMRouter._prepareRoutingInput(globals)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = LLMRouter;
|
package/src/LLMSession.js
CHANGED
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
const {
|
|
7
7
|
FILTER_SCOPE_CONVERSATION, ROLE_ASSISTANT, ROLE_USER, ROLE_SYSTEM
|
|
8
8
|
} = require('./LLMConsts');
|
|
9
|
+
const stateData = require('./utils/stateData');
|
|
9
10
|
|
|
10
11
|
/** @typedef {import('./Responder').QuickReply} QuickReply */
|
|
11
12
|
/** @typedef {import('./LLM').LLMCallPreset} LLMCallPreset */
|
|
12
13
|
/** @typedef {import('./LLM')} LLM */
|
|
13
14
|
/** @typedef {import('./LLM').LLMLogOptions} LLMLogOptions */
|
|
14
15
|
|
|
16
|
+
/** @typedef {import('./Request')} Request */
|
|
17
|
+
/** @typedef {import('./Responder')} Responder */
|
|
18
|
+
|
|
15
19
|
/** @typedef {'user'|'assistant'} LLMChatRole */
|
|
16
20
|
/** @typedef {'system'} LLMSystemRole */
|
|
17
21
|
/** @typedef {'tool'} LLMToolRole */
|
|
@@ -29,7 +33,9 @@ const {
|
|
|
29
33
|
* @prop {string} args - JSON string
|
|
30
34
|
*/
|
|
31
35
|
|
|
32
|
-
/** @typedef {
|
|
36
|
+
/** @typedef {import('./prompt').Renderer} Renderer */
|
|
37
|
+
|
|
38
|
+
/** @typedef {string|Promise<string>|Renderer} PossiblyAsyncContent */
|
|
33
39
|
|
|
34
40
|
/**
|
|
35
41
|
* @template {LLMRole} [R=LLMRole]
|
|
@@ -155,6 +161,16 @@ const {
|
|
|
155
161
|
* - toJson
|
|
156
162
|
*/
|
|
157
163
|
|
|
164
|
+
/**
|
|
165
|
+
* @typedef {object} SessionOptions
|
|
166
|
+
* @prop {LLMFilter[]} [filters]
|
|
167
|
+
* @prop {SendCallback} [onSend]
|
|
168
|
+
* @prop {Request} [req]
|
|
169
|
+
* @prop {Responder} [res]
|
|
170
|
+
* @prop {object} [data]
|
|
171
|
+
* @prop {LLMCallPreset} [preset]
|
|
172
|
+
*/
|
|
173
|
+
|
|
158
174
|
/**
|
|
159
175
|
* @class LLMSession
|
|
160
176
|
* @implements {PromiseLike<LLMMessage<any>>}
|
|
@@ -165,10 +181,18 @@ class LLMSession {
|
|
|
165
181
|
*
|
|
166
182
|
* @param {LLM} llm
|
|
167
183
|
* @param {(PossiblyAsyncLLMMessage|AsyncLLMMessage)[]} [chat]
|
|
168
|
-
* @param {
|
|
169
|
-
* @param {LLMFilter[]} [filters=[]]
|
|
184
|
+
* @param {SessionOptions} [options]
|
|
170
185
|
*/
|
|
171
|
-
constructor (llm, chat = [],
|
|
186
|
+
constructor (llm, chat = [], options = {}) {
|
|
187
|
+
const {
|
|
188
|
+
onSend = null,
|
|
189
|
+
filters = [],
|
|
190
|
+
data = {},
|
|
191
|
+
req = null,
|
|
192
|
+
res = null,
|
|
193
|
+
preset = undefined
|
|
194
|
+
} = options;
|
|
195
|
+
|
|
172
196
|
this._llm = llm;
|
|
173
197
|
|
|
174
198
|
this._onSend = onSend;
|
|
@@ -194,6 +218,36 @@ class LLMSession {
|
|
|
194
218
|
|
|
195
219
|
/** @type {Map<string,ObjectTool>} */
|
|
196
220
|
this._tools = new Map();
|
|
221
|
+
|
|
222
|
+
this._data = { ...data };
|
|
223
|
+
|
|
224
|
+
this._req = req || llm?.req;
|
|
225
|
+
this._res = res || llm?.res;
|
|
226
|
+
|
|
227
|
+
this._preset = preset;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
*
|
|
232
|
+
* @param {object} data
|
|
233
|
+
* @returns {this}
|
|
234
|
+
*/
|
|
235
|
+
setData (data) {
|
|
236
|
+
this._job(() => {
|
|
237
|
+
Object.assign(this._data, data);
|
|
238
|
+
}, true);
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
_resolveData () {
|
|
243
|
+
const dataFromReqRes = this._req || this._res
|
|
244
|
+
? stateData(this._req, this._res)
|
|
245
|
+
: {};
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
...dataFromReqRes,
|
|
249
|
+
...this._data
|
|
250
|
+
};
|
|
197
251
|
}
|
|
198
252
|
|
|
199
253
|
_job (task, runNowAndSyncWhenQueueIsEmpty = false) {
|
|
@@ -447,11 +501,15 @@ class LLMSession {
|
|
|
447
501
|
return;
|
|
448
502
|
}
|
|
449
503
|
if ('content' in m
|
|
504
|
+
&& typeof m.content !== 'function'
|
|
450
505
|
&& (m.content instanceof Promise
|
|
451
506
|
|| (typeof m.content !== 'string' && m.content && 'then' in m.content))) {
|
|
452
507
|
return;
|
|
453
508
|
}
|
|
454
|
-
if (filtered
|
|
509
|
+
if (filtered
|
|
510
|
+
&& this._filters.length >= 0
|
|
511
|
+
&& 'content' in m
|
|
512
|
+
&& (typeof m.content === 'string' || typeof m.content === 'function')) {
|
|
455
513
|
const content = this._filters.reduce((text, filter) => {
|
|
456
514
|
if (!text) {
|
|
457
515
|
return text;
|
|
@@ -463,7 +521,7 @@ class LLMSession {
|
|
|
463
521
|
}
|
|
464
522
|
const res = filter.filter(text, m.role);
|
|
465
523
|
return res === true ? text : res;
|
|
466
|
-
}, m.content);
|
|
524
|
+
}, typeof m.content === 'function' ? m.content.toString() : m.content);
|
|
467
525
|
|
|
468
526
|
if (typeof content === 'string') {
|
|
469
527
|
// @ts-ignore
|
|
@@ -472,6 +530,11 @@ class LLMSession {
|
|
|
472
530
|
content
|
|
473
531
|
});
|
|
474
532
|
}
|
|
533
|
+
} else if ('content' in m && typeof m.content === 'function') {
|
|
534
|
+
ret.push({
|
|
535
|
+
...m,
|
|
536
|
+
content: m.content.toString()
|
|
537
|
+
});
|
|
475
538
|
} else {
|
|
476
539
|
// @ts-ignore
|
|
477
540
|
ret.push(m);
|
|
@@ -520,6 +583,9 @@ class LLMSession {
|
|
|
520
583
|
if (typeof m.content === 'string') {
|
|
521
584
|
return m.content;
|
|
522
585
|
}
|
|
586
|
+
if (typeof m.content === 'function') {
|
|
587
|
+
return m.content.toString();
|
|
588
|
+
}
|
|
523
589
|
return '<Promise>';
|
|
524
590
|
}
|
|
525
591
|
|
|
@@ -627,12 +693,17 @@ class LLMSession {
|
|
|
627
693
|
})();
|
|
628
694
|
return wrapped;
|
|
629
695
|
}
|
|
630
|
-
if (!('content' in msg) || !this._contentIsPromise(msg.content)) {
|
|
696
|
+
if (!('content' in msg) || (!this._contentIsPromise(msg.content) && typeof msg.content !== 'function')) {
|
|
631
697
|
return msg;
|
|
632
698
|
}
|
|
699
|
+
|
|
700
|
+
const contentPromise = typeof msg.content === 'function'
|
|
701
|
+
? msg.content(this._resolveData())
|
|
702
|
+
: msg.content;
|
|
703
|
+
|
|
633
704
|
const ret = {
|
|
634
705
|
...msg,
|
|
635
|
-
content: Promise.resolve(
|
|
706
|
+
content: Promise.resolve(contentPromise)
|
|
636
707
|
.then((r) => {
|
|
637
708
|
// @ts-ignore
|
|
638
709
|
ret.content = r;
|
|
@@ -688,7 +759,7 @@ class LLMSession {
|
|
|
688
759
|
|
|
689
760
|
/**
|
|
690
761
|
*
|
|
691
|
-
* @param {string|Promise<string
|
|
762
|
+
* @param {string|Promise<string>|Renderer} content
|
|
692
763
|
* @returns {this}
|
|
693
764
|
*/
|
|
694
765
|
user (content) {
|
|
@@ -701,7 +772,7 @@ class LLMSession {
|
|
|
701
772
|
|
|
702
773
|
/**
|
|
703
774
|
*
|
|
704
|
-
* @param {string|Promise<string
|
|
775
|
+
* @param {string|Promise<string>|Renderer} content
|
|
705
776
|
* @returns {this}
|
|
706
777
|
*/
|
|
707
778
|
assistant (content) {
|
|
@@ -714,7 +785,7 @@ class LLMSession {
|
|
|
714
785
|
|
|
715
786
|
/**
|
|
716
787
|
*
|
|
717
|
-
* @param {string|Promise<string
|
|
788
|
+
* @param {string|Promise<string>|Renderer} content
|
|
718
789
|
* @returns {this}
|
|
719
790
|
*/
|
|
720
791
|
systemPrompt (content) {
|
|
@@ -747,7 +818,7 @@ class LLMSession {
|
|
|
747
818
|
* @param {LLMLogOptions} [logOptions]
|
|
748
819
|
* @returns {this}
|
|
749
820
|
*/
|
|
750
|
-
generate (providerOptions =
|
|
821
|
+
generate (providerOptions = this._preset, logOptions = {}) {
|
|
751
822
|
this._job(() => this._generate(providerOptions, logOptions));
|
|
752
823
|
return this;
|
|
753
824
|
}
|
|
@@ -759,7 +830,7 @@ class LLMSession {
|
|
|
759
830
|
* @param {LLMLogOptions} [logOptions]
|
|
760
831
|
* @returns {this}
|
|
761
832
|
*/
|
|
762
|
-
generateStructured (output, providerOptions =
|
|
833
|
+
generateStructured (output, providerOptions = this._preset, logOptions = {}) {
|
|
763
834
|
|
|
764
835
|
const responseFormat = 'toJSON' in output && typeof output.toJSON === 'function'
|
|
765
836
|
? output.toJSON()
|
|
@@ -787,7 +858,7 @@ class LLMSession {
|
|
|
787
858
|
* @param {LLMLogOptions} [logOptions]
|
|
788
859
|
* @returns {Promise<LLMMessage<any>>}
|
|
789
860
|
*/
|
|
790
|
-
async _generate (providerOptions =
|
|
861
|
+
async _generate (providerOptions = this._preset, logOptions = {}) {
|
|
791
862
|
let result = await this._llm.generate(this, providerOptions, logOptions);
|
|
792
863
|
|
|
793
864
|
if (result.toolCalls?.length) {
|
package/src/Processor.js
CHANGED
|
@@ -13,6 +13,7 @@ const ReturnSender = require('./ReturnSender');
|
|
|
13
13
|
const { prepareState, mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
14
14
|
const LLM = require('./LLM');
|
|
15
15
|
const LLMMockProvider = require('./LLMMockProvider');
|
|
16
|
+
const LLMDispatcher = require('./LLMDispatcher');
|
|
16
17
|
|
|
17
18
|
/** @typedef {import('./wingbot/CustomEntityDetectionModel').Intent} Intent */
|
|
18
19
|
/** @typedef {import('./ReducerWrapper')} ReducerWrapper */
|
|
@@ -140,9 +141,9 @@ const LLMMockProvider = require('./LLMMockProvider');
|
|
|
140
141
|
*/
|
|
141
142
|
|
|
142
143
|
/**
|
|
143
|
-
* @template {ReducerWrapper|Router|BuildRouter}
|
|
144
|
+
* @template {ReducerWrapper|Router|BuildRouter} R
|
|
144
145
|
* @callback Preloader
|
|
145
|
-
* @param {
|
|
146
|
+
* @param {R} router
|
|
146
147
|
* @param {Ai} ai
|
|
147
148
|
* @returns {Promise<void>}
|
|
148
149
|
*/
|
|
@@ -268,12 +269,11 @@ class Processor extends EventEmitter {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
if (dispatchSync) {
|
|
271
|
-
let previousAction;
|
|
272
272
|
return Promise.resolve(data)
|
|
273
273
|
.then((resolvedData) => {
|
|
274
274
|
let reduceResult;
|
|
275
275
|
Object.assign(resolvedData, { _localpostback: true });
|
|
276
|
-
|
|
276
|
+
req.dispatcher.overrideAction({ action, data: resolvedData });
|
|
277
277
|
if (typeof this.reducer === 'function') {
|
|
278
278
|
// @ts-ignore
|
|
279
279
|
reduceResult = this.reducer(req, res, postBack);
|
|
@@ -283,7 +283,7 @@ class Processor extends EventEmitter {
|
|
|
283
283
|
return reduceResult;
|
|
284
284
|
})
|
|
285
285
|
.then((reduceResult) => {
|
|
286
|
-
req.
|
|
286
|
+
req.dispatcher.overrideAction(null);
|
|
287
287
|
return reduceResult;
|
|
288
288
|
});
|
|
289
289
|
}
|
|
@@ -407,7 +407,7 @@ class Processor extends EventEmitter {
|
|
|
407
407
|
|
|
408
408
|
/** @type {LLMGlobalConfig} */
|
|
409
409
|
const llmConfiguration = {
|
|
410
|
-
provider: new LLMMockProvider(),
|
|
410
|
+
provider: new LLMMockProvider(message.llmMocks),
|
|
411
411
|
...this.options.llm
|
|
412
412
|
};
|
|
413
413
|
|
|
@@ -533,10 +533,11 @@ class Processor extends EventEmitter {
|
|
|
533
533
|
? Request.text('none', text)
|
|
534
534
|
: text;
|
|
535
535
|
// @ts-ignore
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
536
|
+
const dispatcher = new LLMDispatcher(Ai.ai, this.reducer.routing, {
|
|
537
|
+
log: this.options.log
|
|
538
|
+
});
|
|
539
|
+
const req = new Request(request, { lang }, pageId, dispatcher);
|
|
540
|
+
await dispatcher.dispatch(req);
|
|
540
541
|
const actions = req.aiActions();
|
|
541
542
|
|
|
542
543
|
if (actions.length === 0 && allowEmptyAction && req.intents.length > 0) {
|
|
@@ -555,6 +556,7 @@ class Processor extends EventEmitter {
|
|
|
555
556
|
];
|
|
556
557
|
}
|
|
557
558
|
|
|
559
|
+
// @ts-ignore
|
|
558
560
|
return actions
|
|
559
561
|
.map((a) => ({
|
|
560
562
|
...a,
|
|
@@ -638,13 +640,14 @@ class Processor extends EventEmitter {
|
|
|
638
640
|
}
|
|
639
641
|
}
|
|
640
642
|
|
|
641
|
-
|
|
643
|
+
const dispatcher = new LLMDispatcher(Ai.ai, this.reducer.routing, llm, {
|
|
644
|
+
log: this.options.log
|
|
645
|
+
});
|
|
642
646
|
req = new Request(
|
|
643
647
|
message,
|
|
644
648
|
state,
|
|
645
649
|
pageId,
|
|
646
|
-
|
|
647
|
-
this.reducer.globalIntents,
|
|
650
|
+
dispatcher,
|
|
648
651
|
{
|
|
649
652
|
apiUrl: this.options.apiUrl,
|
|
650
653
|
secret: this.options.secret,
|
|
@@ -719,6 +722,7 @@ class Processor extends EventEmitter {
|
|
|
719
722
|
senderMeta,
|
|
720
723
|
llm
|
|
721
724
|
);
|
|
725
|
+
llm.setReqRes(req, res);
|
|
722
726
|
const postBack = this._createPostBack(postbackAcumulator, req, res, features);
|
|
723
727
|
|
|
724
728
|
let continueDispatching = true;
|
|
@@ -738,7 +742,7 @@ class Processor extends EventEmitter {
|
|
|
738
742
|
|
|
739
743
|
await Promise.all([
|
|
740
744
|
preloadPromise,
|
|
741
|
-
|
|
745
|
+
dispatcher.dispatch(req, res)
|
|
742
746
|
]);
|
|
743
747
|
|
|
744
748
|
// @deprecated backward compatibility
|
package/src/ReducerWrapper.js
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
const EventEmitter = require('events');
|
|
7
|
+
const LLMDispatcher = require('./LLMDispatcher');
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('./Responder')} Responder */
|
|
10
|
+
/** @typedef {import('./Request')} Request */
|
|
11
|
+
/** @typedef {import('./LLMDispatcher').Routing} Routing */
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Solution for catching events. This is useful for analytics.
|
|
@@ -39,6 +44,25 @@ class ReducerWrapper extends EventEmitter {
|
|
|
39
44
|
this.processMessage = null;
|
|
40
45
|
|
|
41
46
|
this.setMaxListeners(Infinity);
|
|
47
|
+
|
|
48
|
+
this.globalIntents = new Map();
|
|
49
|
+
|
|
50
|
+
/** @type {Routing} */
|
|
51
|
+
this._routing = null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
resetRouter () {
|
|
55
|
+
this._routing = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @returns {Routing}
|
|
60
|
+
*/
|
|
61
|
+
get routing () {
|
|
62
|
+
if (this._routing === null) {
|
|
63
|
+
this._routing = LLMDispatcher.prepareRouting(this.globalIntents);
|
|
64
|
+
}
|
|
65
|
+
return this._routing;
|
|
42
66
|
}
|
|
43
67
|
|
|
44
68
|
/**
|