wingbot 3.74.7 → 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.
@@ -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 {string|Promise<string>} PossiblyAsyncContent */
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 {SendCallback} [onSend]
169
- * @param {LLMFilter[]} [filters=[]]
184
+ * @param {SessionOptions} [options]
170
185
  */
171
- constructor (llm, chat = [], onSend = null, filters = []) {
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 && this._filters.length >= 0 && 'content' in m && typeof m.content === 'string') {
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(msg.content)
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>} content
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>} content
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>} content
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 = undefined, logOptions = {}) {
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 = undefined, logOptions = {}) {
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 = undefined, logOptions = {}) {
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} T
144
+ * @template {ReducerWrapper|Router|BuildRouter} R
144
145
  * @callback Preloader
145
- * @param {T} router
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
- previousAction = req.setAction(action, resolvedData);
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.setAction(previousAction);
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 req = new Request(request, { lang }, pageId, this.reducer.globalIntents);
537
-
538
- await Ai.ai.preloadAi(req);
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
- // @ts-ignore
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
- // @ts-ignore
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
- Ai.ai.preloadAi(req, res)
745
+ dispatcher.dispatch(req, res)
742
746
  ]);
743
747
 
744
748
  // @deprecated backward compatibility
@@ -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
  /**