wingbot 3.76.18 → 3.77.0-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/.claude/settings.local.json +11 -0
- package/index.js +2 -0
- package/package.json +2 -2
- package/src/BuildRouter.js +31 -13
- package/src/CallbackAuditLog.js +1 -5
- package/src/ChatGpt.js +46 -18
- package/src/ConversationTester.js +1 -5
- package/src/LLM.js +73 -8
- package/src/LLMDispatcher.js +1 -5
- package/src/LLMSession.js +49 -2
- package/src/LLMToolRunner.js +580 -0
- package/src/Plugins.js +53 -0
- package/src/Processor.js +26 -10
- package/src/Responder.js +25 -0
- package/src/analytics/GA4.js +4 -4
- package/src/graphApi/GraphApi.js +1 -5
- package/src/graphApi/gqlRules.js +3 -7
- package/src/log/types.js +161 -0
- package/src/notifications/Notifications.js +1 -5
- package/src/resolvers/index.js +2 -0
- package/src/resolvers/llmTool.js +41 -0
- package/src/resolvers/message.js +51 -5
- package/src/utils/canaryLog.js +1 -6
- package/src/utils/logSpan.js +36 -0
- package/src/utils/pathUtils.js +8 -2
- package/src/wingbot/CachedModel.js +15 -3
- package/src/wingbot/CustomEntityDetectionModel.js +3 -3
- package/src/wingbot/WingbotModel.js +3 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npx mocha:*)",
|
|
5
|
+
"Bash(npx tsc *)",
|
|
6
|
+
"Bash(npm test *)",
|
|
7
|
+
"Bash(node -e \"const p = require\\('./bot/plugins/BTMLLM'\\); console.log\\('factory type:', typeof p\\); console.log\\('factory name:', p.name\\);\")",
|
|
8
|
+
"Bash(grep -n \"prompt\\\\`\\\\|tagged\\\\|render\\\\|compile\\\\|hbs\" /Users/ondrejveres/Wingbot/wingbot-llm/src/prompt.js)"
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
}
|
package/index.js
CHANGED
|
@@ -70,6 +70,7 @@ const LLM = require('./src/LLM');
|
|
|
70
70
|
const LLMSession = require('./src/LLMSession');
|
|
71
71
|
const LLMTool = require('./src/LLMTool');
|
|
72
72
|
const LLMType = require('./src/LLMType');
|
|
73
|
+
const LLMToolRunner = require('./src/LLMToolRunner');
|
|
73
74
|
const LLMRouter = require('./src/LLMRouter');
|
|
74
75
|
const prompt = require('./src/prompt');
|
|
75
76
|
|
|
@@ -167,6 +168,7 @@ module.exports = {
|
|
|
167
168
|
LLMSession,
|
|
168
169
|
LLMTool,
|
|
169
170
|
LLMType,
|
|
171
|
+
LLMToolRunner,
|
|
170
172
|
|
|
171
173
|
LLMRouter,
|
|
172
174
|
prompt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wingbot",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.77.0-alpha.1",
|
|
4
4
|
"description": "Enterprise Messaging Bot Conversation Engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"optionalDependencies": {
|
|
77
77
|
"axios": "^1.6.4",
|
|
78
|
-
"handlebars": "
|
|
78
|
+
"handlebars": "4.7.10"
|
|
79
79
|
},
|
|
80
80
|
"allowScripts": {
|
|
81
81
|
"fsevents@2.3.2": true,
|
package/src/BuildRouter.js
CHANGED
|
@@ -157,12 +157,7 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
|
|
|
157
157
|
* @returns {string}
|
|
158
158
|
*/
|
|
159
159
|
|
|
160
|
-
/**
|
|
161
|
-
* @typedef {object} ILogger
|
|
162
|
-
* @prop {Function} log
|
|
163
|
-
* @prop {Function} warn
|
|
164
|
-
* @prop {Function} error
|
|
165
|
-
*/
|
|
160
|
+
/** @typedef {import('./log/types').LevelLogger} ILogger */
|
|
166
161
|
|
|
167
162
|
/**
|
|
168
163
|
* @template {BaseConfiguration} [C=object]
|
|
@@ -206,11 +201,7 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
|
|
|
206
201
|
* @prop {string|number} [resolverId] - only for text messages with random characters
|
|
207
202
|
*/
|
|
208
203
|
|
|
209
|
-
/**
|
|
210
|
-
* @typedef {object} Logger
|
|
211
|
-
* @prop {Function} log
|
|
212
|
-
* @prop {Function} error
|
|
213
|
-
*/
|
|
204
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
214
205
|
|
|
215
206
|
/**
|
|
216
207
|
* @typedef {object} RunningReq
|
|
@@ -400,6 +391,30 @@ class BuildRouter extends Router {
|
|
|
400
391
|
return this._botLoaded;
|
|
401
392
|
}
|
|
402
393
|
|
|
394
|
+
/**
|
|
395
|
+
* What an expired request was about, without the whole request and
|
|
396
|
+
* responder (they hold the state and the transcript).
|
|
397
|
+
*
|
|
398
|
+
* @param {any[]} args - the reduce() arguments: (req, res, ...)
|
|
399
|
+
* @param {number} timeoutMs
|
|
400
|
+
* @returns {object}
|
|
401
|
+
*/
|
|
402
|
+
static _expireInfo (args, timeoutMs) {
|
|
403
|
+
const [req] = Array.isArray(args) ? args : [];
|
|
404
|
+
let action = null;
|
|
405
|
+
try {
|
|
406
|
+
action = req && typeof req.action === 'function' ? req.action() : null;
|
|
407
|
+
} catch (e) {
|
|
408
|
+
// the action is only a hint
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
senderId: (req && req.senderId) || null,
|
|
412
|
+
pageId: (req && req.pageId) || null,
|
|
413
|
+
action,
|
|
414
|
+
timeoutMs
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
403
418
|
async reduce (...args) {
|
|
404
419
|
await this.preload();
|
|
405
420
|
|
|
@@ -411,7 +426,7 @@ class BuildRouter extends Router {
|
|
|
411
426
|
const timeoutPromise = new Promise((resolve) => {
|
|
412
427
|
timeout = setTimeout(() => {
|
|
413
428
|
timeout = null;
|
|
414
|
-
this._logger.error('BuildRouter EXPIRE',
|
|
429
|
+
this._logger.error('BuildRouter EXPIRE', BuildRouter._expireInfo(args, this.runningReqTimeout));
|
|
415
430
|
this._runningReqs = this._runningReqs
|
|
416
431
|
.filter((rr) => rr !== runningRequest);
|
|
417
432
|
resolve();
|
|
@@ -527,7 +542,10 @@ class BuildRouter extends Router {
|
|
|
527
542
|
this._runningReqs
|
|
528
543
|
.filter((req) => {
|
|
529
544
|
if (req.started < threshold) {
|
|
530
|
-
this._logger.error('BuildRouter EXPIRE filter',
|
|
545
|
+
this._logger.error('BuildRouter EXPIRE filter', {
|
|
546
|
+
...BuildRouter._expireInfo(req.args, this.runningReqTimeout),
|
|
547
|
+
startedAt: new Date(req.started).toISOString()
|
|
548
|
+
});
|
|
531
549
|
return false;
|
|
532
550
|
}
|
|
533
551
|
return true;
|
package/src/CallbackAuditLog.js
CHANGED
|
@@ -35,11 +35,7 @@ const TYPE_INFO = 'Info';
|
|
|
35
35
|
* @prop {string} [ro] - referrer || origin
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* @typedef {object} Logger
|
|
40
|
-
* @prop {Function} log
|
|
41
|
-
* @prop {Function} error
|
|
42
|
-
*/
|
|
38
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
43
39
|
|
|
44
40
|
/**
|
|
45
41
|
* @typedef {object} AuditLogEntry
|
package/src/ChatGpt.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
const nodeFetch = require('node-fetch').default;
|
|
7
|
-
const util = require('util');
|
|
8
7
|
const { PHONE_REGEX, EMAIL_REGEX } = require('./systemEntities/regexps');
|
|
9
8
|
const LLM = require('./LLM');
|
|
10
9
|
|
|
@@ -101,11 +100,7 @@ const LLM = require('./LLM');
|
|
|
101
100
|
* @prop {ChatGPTUsage} usage
|
|
102
101
|
*/
|
|
103
102
|
|
|
104
|
-
/**
|
|
105
|
-
* @typedef {object} Logger
|
|
106
|
-
* @prop {Function} log
|
|
107
|
-
* @prop {Function} error
|
|
108
|
-
*/
|
|
103
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
109
104
|
|
|
110
105
|
/**
|
|
111
106
|
* @typedef {object} GptToolCall
|
|
@@ -178,6 +173,9 @@ const LLM = require('./LLM');
|
|
|
178
173
|
/**
|
|
179
174
|
* @class ChatGpt
|
|
180
175
|
*/
|
|
176
|
+
// how much of the input and the output the info row of an LLM call keeps
|
|
177
|
+
const SUMMARY_TEXT_LENGTH = 2000;
|
|
178
|
+
|
|
181
179
|
class ChatGpt {
|
|
182
180
|
|
|
183
181
|
/**
|
|
@@ -242,15 +240,41 @@ class ChatGpt {
|
|
|
242
240
|
return this._options.model;
|
|
243
241
|
}
|
|
244
242
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
243
|
+
/**
|
|
244
|
+
* The info row of one LLM call: the new input (the last message, not the
|
|
245
|
+
* whole transcript), the output and the usage. The whole request and
|
|
246
|
+
* response are in the debug rows.
|
|
247
|
+
*
|
|
248
|
+
* @param {object} body - the request body
|
|
249
|
+
* @param {ChatGPTChoice} choice
|
|
250
|
+
* @param {LLMUsage} usage
|
|
251
|
+
* @param {number} durationMs
|
|
252
|
+
* @returns {object}
|
|
253
|
+
*/
|
|
254
|
+
static _summary (body, choice, usage, durationMs) {
|
|
255
|
+
const { messages = [], tools = [] } = body;
|
|
256
|
+
const last = messages[messages.length - 1] || {};
|
|
257
|
+
const { message = {}, finish_reason: finishReason } = choice || {};
|
|
258
|
+
const cut = (text) => (typeof text === 'string' && text.length > SUMMARY_TEXT_LENGTH
|
|
259
|
+
? `${text.slice(0, SUMMARY_TEXT_LENGTH)}… (${text.length - SUMMARY_TEXT_LENGTH} more chars)`
|
|
260
|
+
: text);
|
|
261
|
+
return {
|
|
262
|
+
...usage,
|
|
263
|
+
durationMs,
|
|
264
|
+
finishReason,
|
|
265
|
+
messageCount: messages.length,
|
|
266
|
+
toolCount: tools.length,
|
|
267
|
+
input: { role: last.role, content: cut(last.content) },
|
|
268
|
+
output: cut(message.content),
|
|
269
|
+
...(message.tool_calls && message.tool_calls.length
|
|
270
|
+
? {
|
|
271
|
+
toolCalls: message.tool_calls.map((tc) => ({
|
|
272
|
+
name: tc.function && tc.function.name,
|
|
273
|
+
args: cut(tc.function && tc.function.arguments)
|
|
274
|
+
}))
|
|
275
|
+
}
|
|
276
|
+
: {})
|
|
277
|
+
};
|
|
254
278
|
}
|
|
255
279
|
|
|
256
280
|
/**
|
|
@@ -457,7 +481,8 @@ class ChatGpt {
|
|
|
457
481
|
|
|
458
482
|
const apiUrl = `${this._openAiEndpoint}/chat/completions${this._apiKey ? `?api-version=${encodeURIComponent(this._apiVersion)}` : ''}`;
|
|
459
483
|
|
|
460
|
-
|
|
484
|
+
// the whole prompt: verbose only (or replayed with an error)
|
|
485
|
+
this._logger.debug('#GPT request', body);
|
|
461
486
|
|
|
462
487
|
const startTime = Date.now();
|
|
463
488
|
const response = await this._fetch(apiUrl, {
|
|
@@ -508,11 +533,14 @@ class ChatGpt {
|
|
|
508
533
|
|
|
509
534
|
const durationMs = Date.now() - startTime;
|
|
510
535
|
|
|
511
|
-
this.
|
|
536
|
+
this._logger.debug('#GPT response body', { responseData, durationMs });
|
|
537
|
+
this._logger.info('#GPT response', ChatGpt._summary(body, choice, llmUsage, durationMs));
|
|
512
538
|
|
|
513
539
|
return { choice, usage: llmUsage };
|
|
514
540
|
} catch (e) {
|
|
515
|
-
|
|
541
|
+
// the caller logs the error once (LLMSession: the fallback message);
|
|
542
|
+
// the detail goes with it (replayed with the error)
|
|
543
|
+
this._logger.debug('#GPT failed', {
|
|
516
544
|
message: e.message, body, responseText, responseData, status, statusText
|
|
517
545
|
});
|
|
518
546
|
throw e;
|
|
@@ -95,11 +95,7 @@ const DEFAULT_TEXT_THRESHOLD = 0.8;
|
|
|
95
95
|
* @prop {number} stepCount
|
|
96
96
|
*/
|
|
97
97
|
|
|
98
|
-
/**
|
|
99
|
-
* @typedef {object} Logger
|
|
100
|
-
* @prop {Function} log
|
|
101
|
-
* @prop {Function} error
|
|
102
|
-
*/
|
|
98
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
103
99
|
|
|
104
100
|
/**
|
|
105
101
|
* Automated Conversation tests runner
|
package/src/LLM.js
CHANGED
|
@@ -60,6 +60,7 @@ const LLMSession = require('./LLMSession');
|
|
|
60
60
|
* @typedef {object} PrepocessedRuleData
|
|
61
61
|
* @prop {Function} condition
|
|
62
62
|
* @prop {PreprocessorOutput} rule
|
|
63
|
+
* @prop {string[]} [aiTags] - original rule definition (from the designer), for logs
|
|
63
64
|
*/
|
|
64
65
|
|
|
65
66
|
/**
|
|
@@ -75,12 +76,24 @@ const LLMSession = require('./LLMSession');
|
|
|
75
76
|
* @typedef {RuleScore & PreprocessedRule} RuleWithScore
|
|
76
77
|
*/
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* @typedef {object} EvaluatedRule
|
|
81
|
+
* @prop {string|number} [id]
|
|
82
|
+
* @prop {EvaluationRuleAction} [action]
|
|
83
|
+
* @prop {string[]} [aiTags]
|
|
84
|
+
* @prop {boolean} conditionPassed - false, when the rule was skipped by its condition
|
|
85
|
+
* @prop {number} score - 0, when the rule did not match at all
|
|
86
|
+
* @prop {boolean} matched - score reached the threshold
|
|
87
|
+
*/
|
|
88
|
+
|
|
78
89
|
/**
|
|
79
90
|
* @typedef {object} EvaluationResult
|
|
80
91
|
* @prop {string} action
|
|
81
92
|
* @prop {boolean} discard
|
|
82
93
|
* @prop {RuleWithScore[]} results
|
|
83
94
|
* @prop {object} setState
|
|
95
|
+
* @prop {number} [threshold] - minimal score of a matching rule
|
|
96
|
+
* @prop {EvaluatedRule[]} [evaluated] - every rule and the decision made about it
|
|
84
97
|
*/
|
|
85
98
|
|
|
86
99
|
/**
|
|
@@ -190,11 +203,7 @@ const LLMSession = require('./LLMSession');
|
|
|
190
203
|
* @prop {LogPrompt} logPrompt
|
|
191
204
|
*/
|
|
192
205
|
|
|
193
|
-
/**
|
|
194
|
-
* @typedef {object} Logger
|
|
195
|
-
* @prop {Function} log
|
|
196
|
-
* @prop {Function} error
|
|
197
|
-
*/
|
|
206
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
198
207
|
|
|
199
208
|
/**
|
|
200
209
|
* @typedef {object} VectorSearchDocument
|
|
@@ -527,6 +536,8 @@ class LLM {
|
|
|
527
536
|
|
|
528
537
|
return {
|
|
529
538
|
...rest,
|
|
539
|
+
// kept, so logs can show the rule as it was defined in the designer
|
|
540
|
+
aiTags,
|
|
530
541
|
condition,
|
|
531
542
|
rule
|
|
532
543
|
};
|
|
@@ -559,14 +570,27 @@ class LLM {
|
|
|
559
570
|
const setState = {};
|
|
560
571
|
|
|
561
572
|
const sAct = Object.values(LLM.EVALUATION_ACTIONS);
|
|
573
|
+
const { threshold } = this._ai;
|
|
574
|
+
|
|
575
|
+
/** @type {EvaluatedRule[]} */
|
|
576
|
+
const evaluated = [];
|
|
562
577
|
|
|
563
578
|
const results = rules
|
|
564
|
-
.filter((rule) =>
|
|
579
|
+
.filter((rule) => {
|
|
580
|
+
const conditionPassed = !!rule.condition(req, res);
|
|
581
|
+
if (!conditionPassed) {
|
|
582
|
+
evaluated.push(LLM._describeRule(rule, false, 0, threshold));
|
|
583
|
+
}
|
|
584
|
+
return conditionPassed;
|
|
585
|
+
})
|
|
565
586
|
.map((rule) => {
|
|
566
587
|
const matched = this._ai.matcher
|
|
567
588
|
.matchText(text, rule.rule, nlpResult, state);
|
|
568
589
|
|
|
569
|
-
|
|
590
|
+
const score = matched ? matched.score : 0;
|
|
591
|
+
evaluated.push(LLM._describeRule(rule, true, score, threshold));
|
|
592
|
+
|
|
593
|
+
if (!matched || matched.score < threshold) {
|
|
570
594
|
return null;
|
|
571
595
|
}
|
|
572
596
|
|
|
@@ -594,10 +618,51 @@ class LLM {
|
|
|
594
618
|
setState,
|
|
595
619
|
results,
|
|
596
620
|
discard,
|
|
597
|
-
action: discard ? null : topRankedAction
|
|
621
|
+
action: discard ? null : topRankedAction,
|
|
622
|
+
threshold,
|
|
623
|
+
evaluated
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Rule as a plain object for logs (RegExp objects serialize as {})
|
|
629
|
+
*
|
|
630
|
+
* @param {PreprocessedRule} rule
|
|
631
|
+
* @param {boolean} conditionPassed
|
|
632
|
+
* @param {number} score
|
|
633
|
+
* @param {number} threshold
|
|
634
|
+
* @returns {EvaluatedRule}
|
|
635
|
+
*/
|
|
636
|
+
static _describeRule (rule, conditionPassed, score, threshold) {
|
|
637
|
+
// @ts-ignore
|
|
638
|
+
const { id, action = null, aiTags } = rule;
|
|
639
|
+
return {
|
|
640
|
+
id,
|
|
641
|
+
action,
|
|
642
|
+
aiTags: aiTags || LLM._ruleTags(rule.rule),
|
|
643
|
+
conditionPassed,
|
|
644
|
+
score,
|
|
645
|
+
matched: conditionPassed && score >= threshold
|
|
598
646
|
};
|
|
599
647
|
}
|
|
600
648
|
|
|
649
|
+
/**
|
|
650
|
+
* Fallback for rules added without their definition
|
|
651
|
+
*
|
|
652
|
+
* @param {PreprocessorOutput} rule
|
|
653
|
+
* @returns {string[]}
|
|
654
|
+
*/
|
|
655
|
+
static _ruleTags (rule) {
|
|
656
|
+
if (!rule) return [];
|
|
657
|
+
/** @type {any} */
|
|
658
|
+
const r = rule;
|
|
659
|
+
return [
|
|
660
|
+
...(r.regexps || []).map((x) => `regexp:${x.r instanceof RegExp ? x.r.source : x.r}`),
|
|
661
|
+
...(r.intents || []).map((i) => `intent:${i}`),
|
|
662
|
+
...(r.entities || []).map((e) => `@${e.entity}${e.optional ? '?' : ''}`)
|
|
663
|
+
];
|
|
664
|
+
}
|
|
665
|
+
|
|
601
666
|
}
|
|
602
667
|
|
|
603
668
|
module.exports = LLM;
|
package/src/LLMDispatcher.js
CHANGED
|
@@ -39,11 +39,7 @@ const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{
|
|
|
39
39
|
* @prop {RouterResolver} resolve
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* @typedef {object} Logger
|
|
44
|
-
* @prop {Function} log
|
|
45
|
-
* @prop {Function} error
|
|
46
|
-
*/
|
|
42
|
+
/** @typedef {import('./log/types').LevelLogger} Logger */
|
|
47
43
|
|
|
48
44
|
/**
|
|
49
45
|
* @typedef {object} DispatcherOptions
|
package/src/LLMSession.js
CHANGED
|
@@ -101,6 +101,7 @@ const stateData = require('./utils/stateData');
|
|
|
101
101
|
/**
|
|
102
102
|
* @callback ToolFnCallback
|
|
103
103
|
* @param {{[key: string]: any}} input
|
|
104
|
+
* @param {LLMSession} [session] - call `session.stop()` to end the generation after the tool
|
|
104
105
|
* @returns {string|Promise<string>}
|
|
105
106
|
*/
|
|
106
107
|
|
|
@@ -256,6 +257,8 @@ class LLMSession {
|
|
|
256
257
|
|
|
257
258
|
/** @type {PossiblyAsyncContent|null} */
|
|
258
259
|
this._fallbackMessage = null;
|
|
260
|
+
|
|
261
|
+
this._stopped = false;
|
|
259
262
|
}
|
|
260
263
|
|
|
261
264
|
/**
|
|
@@ -374,6 +377,15 @@ class LLMSession {
|
|
|
374
377
|
.then(onFulfilled, onRejected);
|
|
375
378
|
}
|
|
376
379
|
|
|
380
|
+
/**
|
|
381
|
+
* the preset (or the provider options), the session generates with by default
|
|
382
|
+
*
|
|
383
|
+
* @returns {string|object}
|
|
384
|
+
*/
|
|
385
|
+
get preset () {
|
|
386
|
+
return this._preset;
|
|
387
|
+
}
|
|
388
|
+
|
|
377
389
|
/**
|
|
378
390
|
* @returns {ToolFunction[]}
|
|
379
391
|
*/
|
|
@@ -429,7 +441,7 @@ class LLMSession {
|
|
|
429
441
|
} else if (errors.length) {
|
|
430
442
|
// @ts-ignore
|
|
431
443
|
const errs = errors.map((e) => e.error);
|
|
432
|
-
this._llm.log.
|
|
444
|
+
this._llm.log.debug('LLMSession failures', { errors: errs.map((e) => e.message) });
|
|
433
445
|
throw new Error(errs.map((e) => e.message).join(', '));
|
|
434
446
|
}
|
|
435
447
|
}
|
|
@@ -903,6 +915,28 @@ class LLMSession {
|
|
|
903
915
|
return this;
|
|
904
916
|
}
|
|
905
917
|
|
|
918
|
+
/**
|
|
919
|
+
* Ends the generation right after the current round of tool calls - the model
|
|
920
|
+
* is not asked again and nothing generated in this session is sent.
|
|
921
|
+
*
|
|
922
|
+
* Meant to be called from within a tool, which takes over the conversation
|
|
923
|
+
* (e.g. makes a postback to an interaction). It's applied immediately
|
|
924
|
+
* (not queued), because the tool already runs inside a queued job.
|
|
925
|
+
*
|
|
926
|
+
* @returns {this}
|
|
927
|
+
*/
|
|
928
|
+
stop () {
|
|
929
|
+
this._stopped = true;
|
|
930
|
+
return this;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* @returns {boolean}
|
|
935
|
+
*/
|
|
936
|
+
isStoppedSync () {
|
|
937
|
+
return this._stopped;
|
|
938
|
+
}
|
|
939
|
+
|
|
906
940
|
/**
|
|
907
941
|
* @returns {Promise<string>}
|
|
908
942
|
*/
|
|
@@ -1024,6 +1058,16 @@ class LLMSession {
|
|
|
1024
1058
|
},
|
|
1025
1059
|
...results.filter((r) => !!r)
|
|
1026
1060
|
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (this._stopped) {
|
|
1064
|
+
// a tool took over the conversation - drop any text generated
|
|
1065
|
+
// alongside the tool calls and don't ask the model again
|
|
1066
|
+
result = { role: ROLE_ASSISTANT, content: null };
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
if (toolCalls.length) {
|
|
1027
1071
|
result = await this._llm.generate(this, providerOptions, logOptions);
|
|
1028
1072
|
} else {
|
|
1029
1073
|
// everything failed - force a final text answer without tools
|
|
@@ -1093,7 +1137,7 @@ class LLMSession {
|
|
|
1093
1137
|
let args;
|
|
1094
1138
|
try {
|
|
1095
1139
|
args = JSON.parse(toolCall.args);
|
|
1096
|
-
const fnResult = await Promise.resolve(tool.fn(args));
|
|
1140
|
+
const fnResult = await Promise.resolve(tool.fn(args, this));
|
|
1097
1141
|
|
|
1098
1142
|
/**
|
|
1099
1143
|
* {
|
|
@@ -1193,6 +1237,9 @@ class LLMSession {
|
|
|
1193
1237
|
*/
|
|
1194
1238
|
send (quickReplies = undefined) {
|
|
1195
1239
|
this._job(() => {
|
|
1240
|
+
if (this._stopped) {
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1196
1243
|
const messages = this.messagesToSendSync();
|
|
1197
1244
|
this._onSend(messages, quickReplies);
|
|
1198
1245
|
}, true);
|