wingbot 3.71.5 → 3.72.0
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/.babelrc +0 -0
- package/.github/workflows/deploy.yml +2 -2
- package/.github/workflows/pullRequest.yml +2 -2
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/index.js +0 -0
- package/jsconfig.json +0 -0
- package/package.json +1 -1
- package/plugins/ai.wingbot.clearMessageSequences/plugin.js +0 -0
- package/plugins/ai.wingbot.conditionIfGoBackPossible/plugin.js +0 -0
- package/plugins/ai.wingbot.disambiguation/plugin.js +0 -0
- package/plugins/ai.wingbot.goBack/plugin.js +0 -0
- package/plugins/ai.wingbot.goToLastBreadcrumb/plugin.js +0 -0
- package/plugins/ai.wingbot.ifImageDetected/plugin.js +0 -0
- package/plugins/ai.wingbot.ifStickerDetected/plugin.js +0 -0
- package/plugins/ai.wingbot.jumpBack/plugin.js +0 -0
- package/plugins/ai.wingbot.jumpTo/plugin.js +0 -0
- package/plugins/ai.wingbot.keepInInteraction/plugin.js +0 -0
- package/plugins/ai.wingbot.keepInInteractionJustOnce/plugin.js +0 -0
- package/plugins/ai.wingbot.keepPreviousHandlers/plugin.js +0 -0
- package/plugins/ai.wingbot.keepPreviousHandlersJustOnce/plugin.js +0 -0
- package/plugins/ai.wingbot.oneTimeNotificationRequest/plugin.js +0 -0
- package/plugins/ai.wingbot.openai/plugin.js +0 -0
- package/plugins/ai.wingbot.passThreadToBot/plugin.js +0 -0
- package/plugins/ai.wingbot.persona/plugin.js +0 -0
- package/plugins/ai.wingbot.putABreadcrumb/plugin.js +0 -0
- package/plugins/ai.wingbot.regexp/plugin.js +0 -0
- package/plugins/ai.wingbot.setState/plugin.js +0 -0
- package/plugins/ai.wingbot.setStateFromInput/plugin.js +0 -0
- package/plugins/ai.wingbot.slotsContinue/plugin.js +0 -0
- package/plugins/ai.wingbot.slotsRegister/plugin.js +0 -0
- package/plugins/ai.wingbot.stopResponding/plugin.js +0 -0
- package/plugins/ai.wingbot.trackingEvent/plugin.js +0 -0
- package/plugins/ai.wingbot.upload/plugin.js +0 -0
- package/plugins/ai.wingbot.waitASecond/plugin.js +0 -0
- package/plugins/index.js +0 -0
- package/plugins/plugins.json +0 -0
- package/src/Ai.js +44 -3
- package/src/AiMatching.js +66 -40
- package/src/BatchConversationTester.js +0 -0
- package/src/BotApp.js +0 -0
- package/src/BotAppSender.js +0 -0
- package/src/BuildRouter.js +2 -0
- package/src/CallbackAuditLog.js +0 -0
- package/src/ChatGpt.js +0 -0
- package/src/ConversationTester.js +0 -0
- package/src/LLM.js +184 -3
- package/src/LLMMockProvider.js +0 -0
- package/src/LLMSession.js +90 -3
- package/src/MockAiModel.js +0 -0
- package/src/OrchestratorClient.js +0 -0
- package/src/Plugins.js +0 -0
- package/src/Processor.js +1 -1
- package/src/ReducerWrapper.js +0 -0
- package/src/Request.js +0 -0
- package/src/Responder.js +142 -11
- package/src/ReturnSender.js +4 -4
- package/src/Router.js +0 -0
- package/src/RouterWrap.js +0 -0
- package/src/SecurityMiddleware.js +0 -0
- package/src/Tester.js +44 -2
- package/src/analytics/GA4.js +0 -0
- package/src/analytics/consts.js +0 -0
- package/src/analytics/onInteractionHandler.js +0 -0
- package/src/defaultResourceMap.js +0 -0
- package/src/features.js +0 -0
- package/src/flags.js +0 -0
- package/src/fuzzy/factoryFuzzySearch.js +0 -0
- package/src/fuzzy/fuzzyUtils.js +0 -0
- package/src/fuzzy/index.js +0 -0
- package/src/fuzzy/levenshtein.js +0 -0
- package/src/fuzzy/normalize.js +0 -0
- package/src/fuzzy/prepareFuzzyIndex.js +0 -0
- package/src/graphApi/GraphApi.js +0 -0
- package/src/graphApi/WingbotApiConnector.js +0 -0
- package/src/graphApi/apiAuthorizer.js +0 -0
- package/src/graphApi/conversationTestApi.js +0 -0
- package/src/graphApi/conversationsApi.js +0 -0
- package/src/graphApi/index.js +0 -0
- package/src/graphApi/postBackApi.js +0 -0
- package/src/graphApi/schema.gql +0 -0
- package/src/graphApi/validateBotApi.js +0 -0
- package/src/notifications/Notifications.js +0 -0
- package/src/notifications/NotificationsStorage.js +0 -0
- package/src/notifications/api/index.js +0 -0
- package/src/notifications/api/notificationsApiFactory.js +0 -0
- package/src/notifications/index.js +0 -0
- package/src/resolvers/bounce.js +0 -0
- package/src/resolvers/button.js +0 -0
- package/src/resolvers/carousel.js +0 -0
- package/src/resolvers/contextMessage.js +21 -4
- package/src/resolvers/expected.js +0 -0
- package/src/resolvers/expectedInput.js +0 -0
- package/src/resolvers/hbs.js +0 -0
- package/src/resolvers/include.js +0 -0
- package/src/resolvers/index.js +0 -0
- package/src/resolvers/media.js +0 -0
- package/src/resolvers/message.js +54 -4
- package/src/resolvers/passThread.js +0 -0
- package/src/resolvers/path.js +0 -0
- package/src/resolvers/plugin.js +0 -0
- package/src/resolvers/postback.js +0 -0
- package/src/resolvers/resolverTags.js +0 -0
- package/src/resolvers/setState.js +0 -0
- package/src/resolvers/skip.js +0 -0
- package/src/resolvers/subscribtions.js +0 -0
- package/src/resolvers/utils.js +14 -5
- package/src/systemEntities/email.js +0 -0
- package/src/systemEntities/index.js +0 -0
- package/src/systemEntities/phone.js +0 -0
- package/src/systemEntities/regexps.js +0 -0
- package/src/templates/BaseTemplate.js +0 -0
- package/src/templates/ButtonTemplate.js +0 -0
- package/src/templates/GenericTemplate.js +0 -0
- package/src/templates/ListTemplate.js +0 -0
- package/src/templates/ReceiptTemplate.js +0 -0
- package/src/testTools/AnyResponseAssert.js +0 -0
- package/src/testTools/PromptAssert.js +184 -0
- package/src/testTools/ResponseAssert.js +0 -0
- package/src/testTools/asserts.js +42 -4
- package/src/testTools/index.js +0 -0
- package/src/tools/MemoryBotConfigStorage.js +0 -0
- package/src/tools/MemoryChatLogStorage.js +0 -0
- package/src/tools/MemoryStateStorage.js +0 -0
- package/src/tools/bufferloader.js +0 -0
- package/src/tools/index.js +0 -0
- package/src/tools/routeToEvents.js +0 -0
- package/src/transcript/extractText.js +0 -0
- package/src/transcript/textBodyFromTranscript.js +0 -0
- package/src/transcript/transcriptFromHistory.js +0 -0
- package/src/utils/ai.js +0 -0
- package/src/utils/compileWithState.js +0 -0
- package/src/utils/customCondition.js +14 -1
- package/src/utils/customFn.js +0 -0
- package/src/utils/datetime.js +0 -0
- package/src/utils/deepMapTools.js +0 -0
- package/src/utils/generateToken.js +0 -0
- package/src/utils/getCondition.js +16 -4
- package/src/utils/getUpdate.js +4 -4
- package/src/utils/headersToAuditMeta.js +0 -0
- package/src/utils/index.js +0 -0
- package/src/utils/pathUtils.js +0 -0
- package/src/utils/quickReplies.js +0 -0
- package/src/utils/slots.js +0 -0
- package/src/utils/stateData.js +2 -0
- package/src/utils/stateVariables.js +0 -0
- package/src/utils/tokenizer.js +0 -0
- package/src/utils/wrapPluginFunction.js +0 -0
- package/src/wingbot/CachedModel.js +0 -0
- package/src/wingbot/CustomEntityDetectionModel.js +0 -0
- package/src/wingbot/WingbotModel.js +0 -0
- package/src/wingbot/index.js +0 -0
package/src/Tester.js
CHANGED
|
@@ -19,8 +19,12 @@ 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
21
|
const LLMMockProvider = require('./LLMMockProvider');
|
|
22
|
+
const PromptAssert = require('./testTools/PromptAssert');
|
|
22
23
|
|
|
23
24
|
/** @typedef {import('./Processor').ProcessorOptions<Router>} ProcessorOptions */
|
|
25
|
+
/** @typedef {import('./LLM').PromptInfo} PromptInfo */
|
|
26
|
+
/** @typedef {import('./LLM').LLMRole} LLMRole */
|
|
27
|
+
/** @typedef {import('./LLM').LLMMessage} LLMMessage */
|
|
24
28
|
|
|
25
29
|
/**
|
|
26
30
|
* Utility for testing requests
|
|
@@ -128,6 +132,9 @@ class Tester {
|
|
|
128
132
|
this.responses = [];
|
|
129
133
|
this.actions = [];
|
|
130
134
|
|
|
135
|
+
/** @type {PromptInfo[]} */
|
|
136
|
+
this.prompts = [];
|
|
137
|
+
|
|
131
138
|
/**
|
|
132
139
|
* @prop {object} predefined test data to use
|
|
133
140
|
*/
|
|
@@ -201,6 +208,7 @@ class Tester {
|
|
|
201
208
|
this._actionsCollector = [];
|
|
202
209
|
this._pluginBlocksCollector = [];
|
|
203
210
|
this._responsesMock = [];
|
|
211
|
+
this.prompts = [];
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
/**
|
|
@@ -259,6 +267,7 @@ class Tester {
|
|
|
259
267
|
throw Object.assign(new Error(`Processor failed with status ${res.status}`), { code: res.status });
|
|
260
268
|
}
|
|
261
269
|
this.responses = messageSender.responses;
|
|
270
|
+
this.prompts = messageSender.prompts;
|
|
262
271
|
this.pluginBlocks = this._pluginBlocksCollector;
|
|
263
272
|
this.actions = this._actionsCollector;
|
|
264
273
|
this._actionsCollector = [];
|
|
@@ -388,6 +397,36 @@ class Tester {
|
|
|
388
397
|
this.storage.saveState(stateObj);
|
|
389
398
|
}
|
|
390
399
|
|
|
400
|
+
/**
|
|
401
|
+
*
|
|
402
|
+
* @returns {PromptAssert}
|
|
403
|
+
*/
|
|
404
|
+
anyPrompt () {
|
|
405
|
+
return new PromptAssert(this.prompts);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
*
|
|
410
|
+
* @returns {PromptAssert}
|
|
411
|
+
*/
|
|
412
|
+
lastPrompt () {
|
|
413
|
+
return new PromptAssert(
|
|
414
|
+
this.prompts.length
|
|
415
|
+
? [this.prompts[this.prompts.length - 1]]
|
|
416
|
+
: []
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
*
|
|
422
|
+
* @returns {LLMMessage}
|
|
423
|
+
*/
|
|
424
|
+
getLastPromptResult () {
|
|
425
|
+
return this.prompts.length
|
|
426
|
+
? this.prompts[this.prompts.length - 1].result
|
|
427
|
+
: null;
|
|
428
|
+
}
|
|
429
|
+
|
|
391
430
|
/**
|
|
392
431
|
* Assert, that state contains a subset of provided value
|
|
393
432
|
*
|
|
@@ -606,9 +645,10 @@ class Tester {
|
|
|
606
645
|
/**
|
|
607
646
|
* Prints last conversation turnaround
|
|
608
647
|
*
|
|
609
|
-
* @param {boolean} [
|
|
648
|
+
* @param {boolean} [full=false]
|
|
649
|
+
* @param {boolean} [showPrivateKeys=false]
|
|
610
650
|
*/
|
|
611
|
-
debug (showPrivateKeys = false) {
|
|
651
|
+
debug (full = false, showPrivateKeys = false) {
|
|
612
652
|
// eslint-disable-next-line no-console
|
|
613
653
|
console.log(
|
|
614
654
|
'\n===== actions =====\n',
|
|
@@ -625,6 +665,8 @@ class Tester {
|
|
|
625
665
|
Object.entries(this.getState().state)
|
|
626
666
|
.filter((e) => showPrivateKeys || !e[0].startsWith('_'))
|
|
627
667
|
),
|
|
668
|
+
'\n------- LLM -------\n',
|
|
669
|
+
...PromptAssert.debug(this.prompts, full, true),
|
|
628
670
|
'\n===================\n'
|
|
629
671
|
);
|
|
630
672
|
}
|
package/src/analytics/GA4.js
CHANGED
|
File without changes
|
package/src/analytics/consts.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/features.js
CHANGED
|
File without changes
|
package/src/flags.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/fuzzy/fuzzyUtils.js
CHANGED
|
File without changes
|
package/src/fuzzy/index.js
CHANGED
|
File without changes
|
package/src/fuzzy/levenshtein.js
CHANGED
|
File without changes
|
package/src/fuzzy/normalize.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/graphApi/GraphApi.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/graphApi/index.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/graphApi/schema.gql
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/resolvers/bounce.js
CHANGED
|
File without changes
|
package/src/resolvers/button.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -8,30 +8,47 @@ const Router = require('../Router');
|
|
|
8
8
|
const Responder = require('../Responder');
|
|
9
9
|
const { getLanguageText } = require('./utils');
|
|
10
10
|
const compileWithState = require('../utils/compileWithState');
|
|
11
|
+
const LLM = require('../LLM');
|
|
11
12
|
|
|
12
13
|
/** @typedef {import('../BuildRouter').BotContext<any>} BotContext */
|
|
13
14
|
/** @typedef {import('../Router').Resolver<any>} Resolver */
|
|
14
15
|
/** @typedef {import('./utils').Translations} Translations */
|
|
16
|
+
/** @typedef {import('../LLM').EvaluationRule} EvaluationRule */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} ContextMessageParams
|
|
20
|
+
* @property {Translations} context
|
|
21
|
+
* @property {string} [type]
|
|
22
|
+
* @property {EvaluationRule[]} [rules]
|
|
23
|
+
*/
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
26
|
*
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {Translations} params.context
|
|
20
|
-
* @param {string} [params.type]
|
|
27
|
+
* @param {ContextMessageParams} params
|
|
21
28
|
* @param {BotContext} context
|
|
22
29
|
* @returns {Resolver}
|
|
23
30
|
*/
|
|
24
31
|
// eslint-disable-next-line no-unused-vars
|
|
25
32
|
function contextMessage (params, context) {
|
|
26
33
|
|
|
34
|
+
const { rules = [] } = params;
|
|
35
|
+
const preprocessedRules = LLM.preprocessEvaluationRules(rules, context);
|
|
36
|
+
|
|
27
37
|
return async (req, res) => {
|
|
28
|
-
res.
|
|
38
|
+
res.llmAddInstructions((r) => {
|
|
29
39
|
const translated = getLanguageText(params.context, req.state.lang);
|
|
30
40
|
const translatedText = Array.isArray(translated) ? translated[0] : translated;
|
|
31
41
|
const statefulPrompt = compileWithState(req, r, translatedText);
|
|
32
42
|
return statefulPrompt;
|
|
33
43
|
}, params.type);
|
|
34
44
|
|
|
45
|
+
preprocessedRules.forEach((rule) => res.llmAddResultRule(
|
|
46
|
+
rule,
|
|
47
|
+
undefined,
|
|
48
|
+
undefined,
|
|
49
|
+
params.type
|
|
50
|
+
));
|
|
51
|
+
|
|
35
52
|
return Router.CONTINUE;
|
|
36
53
|
};
|
|
37
54
|
}
|
|
File without changes
|
|
File without changes
|
package/src/resolvers/hbs.js
CHANGED
|
File without changes
|
package/src/resolvers/include.js
CHANGED
|
File without changes
|
package/src/resolvers/index.js
CHANGED
|
File without changes
|
package/src/resolvers/media.js
CHANGED
|
File without changes
|
package/src/resolvers/message.js
CHANGED
|
@@ -20,10 +20,14 @@ const {
|
|
|
20
20
|
FEATURE_SSML, FEATURE_TEXT, FEATURE_VOICE
|
|
21
21
|
} = require('../features');
|
|
22
22
|
const { vars, VAR_TYPES } = require('../utils/stateVariables');
|
|
23
|
+
const LLM = require('../LLM');
|
|
23
24
|
|
|
24
25
|
/** @typedef {import('../Responder').VoiceControl} VoiceControl */
|
|
26
|
+
/** @typedef {import('../BuildRouter').LinksMap} LinksMap */
|
|
25
27
|
/** @typedef {import('./utils').Translations} Translations */
|
|
26
28
|
/** @typedef {import('./utils').TextObject} TextObject */
|
|
29
|
+
/** @typedef {import('../utils/getCondition').ConditionDefinition} ConditionDefinition */
|
|
30
|
+
/** @typedef {import('../utils/getCondition').ConditionContext} ConditionContext */
|
|
27
31
|
/**
|
|
28
32
|
* Returns voice control props from params
|
|
29
33
|
*
|
|
@@ -67,6 +71,30 @@ function getVoiceControlFromParams (params, lang = null) {
|
|
|
67
71
|
return Object.keys(voiceControl).length > 0 ? voiceControl : null;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {object} QuickReplyData
|
|
76
|
+
* @prop {boolean} [isLocation]
|
|
77
|
+
* @prop {boolean} [isEmail]
|
|
78
|
+
* @prop {boolean} [isPhone]
|
|
79
|
+
* @prop {boolean} [trackAsNegative]
|
|
80
|
+
* @prop {string} [action]
|
|
81
|
+
* @prop {string} [targetRouteId]
|
|
82
|
+
* @prop {{l:string,t:string[]}[] | string} [title]
|
|
83
|
+
* @prop {object} [setState]
|
|
84
|
+
* @prop {string[]} [aiTags]
|
|
85
|
+
* @prop {{l:string,t:string[]}[] | string} [aiTitle]
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @typedef {ConditionDefinition & QuickReplyData} QuickReply
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @param {QuickReply[]} replies
|
|
95
|
+
* @param {LinksMap} linksMap
|
|
96
|
+
* @param {ConditionContext} context
|
|
97
|
+
*/
|
|
70
98
|
function parseReplies (replies, linksMap, context) {
|
|
71
99
|
return replies.map((reply) => {
|
|
72
100
|
|
|
@@ -313,8 +341,9 @@ function message (params, context = {}) {
|
|
|
313
341
|
/**
|
|
314
342
|
* @param {Request} req
|
|
315
343
|
* @param {Responder} res
|
|
344
|
+
* @param {Function } postBack
|
|
316
345
|
*/
|
|
317
|
-
return async (req, res) => {
|
|
346
|
+
return async (req, res, postBack) => {
|
|
318
347
|
if (condition === undefined) {
|
|
319
348
|
condition = getCondition(params, context, 'Message condition');
|
|
320
349
|
}
|
|
@@ -417,29 +446,50 @@ function message (params, context = {}) {
|
|
|
417
446
|
}
|
|
418
447
|
|
|
419
448
|
if (params.type === 'prompt') {
|
|
449
|
+
res.typingOn()
|
|
450
|
+
.wait(1000);
|
|
420
451
|
const session = await res.llmSessionWithHistory(params.llmContextType);
|
|
421
452
|
|
|
422
453
|
await session.systemPrompt(text)
|
|
423
|
-
.debug()
|
|
424
454
|
.generate();
|
|
425
455
|
|
|
456
|
+
const evaluation = await res.llmEvaluate(session, params.llmContextType);
|
|
457
|
+
|
|
458
|
+
if (evaluation.discard) {
|
|
459
|
+
if (isLastMessage && !req.actionData()._resolverTag) {
|
|
460
|
+
res.finalMessageSent = true;
|
|
461
|
+
}
|
|
462
|
+
return ret;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (evaluation.action) {
|
|
466
|
+
postBack(evaluation.action);
|
|
467
|
+
return Router.END;
|
|
468
|
+
}
|
|
469
|
+
|
|
426
470
|
// if (!response.content) {
|
|
427
471
|
// // no response?
|
|
428
472
|
// }
|
|
429
473
|
|
|
430
474
|
const messages = session.messagesToSend();
|
|
431
475
|
|
|
476
|
+
res.setFlag(LLM.GPT_FLAG);
|
|
477
|
+
|
|
432
478
|
const lastMessageI = messages.length - 1;
|
|
433
479
|
messages.forEach((m, i) => {
|
|
434
480
|
if (lastMessageI === i) {
|
|
435
481
|
text = m.content;
|
|
436
482
|
} else {
|
|
437
|
-
res.text(m.content
|
|
483
|
+
res.text(m.content, null, null, {
|
|
484
|
+
disableAutoTyping: true
|
|
485
|
+
});
|
|
438
486
|
}
|
|
439
487
|
});
|
|
440
488
|
}
|
|
441
489
|
|
|
442
|
-
res.text(text, sendReplies, voiceControl
|
|
490
|
+
res.text(text, sendReplies, voiceControl, {
|
|
491
|
+
disableAutoTyping: params.type === 'prompt'
|
|
492
|
+
});
|
|
443
493
|
|
|
444
494
|
if (isLastMessage && !req.actionData()._resolverTag) {
|
|
445
495
|
res.finalMessageSent = true;
|
|
File without changes
|
package/src/resolvers/path.js
CHANGED
|
File without changes
|
package/src/resolvers/plugin.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/resolvers/skip.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/resolvers/utils.js
CHANGED
|
@@ -31,17 +31,26 @@ const WEBVIEW_COMPACT = 'compact';
|
|
|
31
31
|
* @prop {string|null} p - purpose
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* null = text+voice
|
|
36
|
+
* t = text only
|
|
37
|
+
* v = voice only
|
|
38
|
+
* s = ssml
|
|
39
|
+
*
|
|
40
|
+
* @typedef {"t"|"v"|"s"|null} Purpose
|
|
41
|
+
*/
|
|
42
|
+
|
|
34
43
|
/**
|
|
35
44
|
* @typedef Translation
|
|
36
45
|
* @property {string|string[]} t - text alternatives
|
|
37
46
|
* @property {string | null} l - language
|
|
38
|
-
* @property {
|
|
39
|
-
* null = default + voice
|
|
40
|
-
* t = text
|
|
41
|
-
* v = voice
|
|
42
|
-
* s = ssml
|
|
47
|
+
* @property {Purpose | Purpose[]} [p] - purposes
|
|
43
48
|
*/
|
|
44
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @param {string | string[] | Translation | Translation[]} translations
|
|
52
|
+
* @returns {translations is Translation[]}
|
|
53
|
+
*/
|
|
45
54
|
function isArrayOfObjects (translations) {
|
|
46
55
|
return Array.isArray(translations)
|
|
47
56
|
&& typeof translations[0] === 'object'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const assert = require('assert');
|
|
7
|
+
const asserts = require('./asserts');
|
|
8
|
+
|
|
9
|
+
const { ex } = asserts;
|
|
10
|
+
|
|
11
|
+
/** @typedef {import('../LLM').PromptInfo} PromptInfo */
|
|
12
|
+
/** @typedef {import('../LLM').LLMMessage} LLMMessage */
|
|
13
|
+
/** @typedef {import('../LLM').LLMRole} LLMRole */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @class PromptAssert
|
|
17
|
+
*/
|
|
18
|
+
class PromptAssert {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {PromptInfo[]} prompts
|
|
23
|
+
*/
|
|
24
|
+
constructor (prompts) {
|
|
25
|
+
this._prompts = prompts;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if recent instruction contains selected string
|
|
30
|
+
*
|
|
31
|
+
* @param {string} search
|
|
32
|
+
* @returns {this}
|
|
33
|
+
*/
|
|
34
|
+
instructionContains (search) {
|
|
35
|
+
const messages = this._flatPrompt((m) => m.role === 'system');
|
|
36
|
+
this._promptContains(search, messages, false, 'of role "system"');
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if recent prompt input contains selected string
|
|
42
|
+
*
|
|
43
|
+
* @param {string} search
|
|
44
|
+
* @returns {this}
|
|
45
|
+
*/
|
|
46
|
+
promptContains (search) {
|
|
47
|
+
this._promptContains(search, this._flatPrompt());
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if recent results contains selected string
|
|
53
|
+
*
|
|
54
|
+
* @param {string} search
|
|
55
|
+
* @returns {this}
|
|
56
|
+
*/
|
|
57
|
+
resultContains (search) {
|
|
58
|
+
this._promptContains(search, this._flatResults());
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {(value: LLMMessage, index: number, array: LLMMessage[]) => unknown} filter
|
|
65
|
+
* @returns {LLMMessage[]}
|
|
66
|
+
*/
|
|
67
|
+
_flatPrompt (filter = () => true) {
|
|
68
|
+
return this._prompts
|
|
69
|
+
.flatMap((prompt) => prompt.prompt
|
|
70
|
+
.filter(filter));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @returns {LLMMessage[]}
|
|
76
|
+
*/
|
|
77
|
+
_flatResults () {
|
|
78
|
+
return this._prompts
|
|
79
|
+
.map((prompt) => prompt.result);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_promptContains (search, messages, notContains = false, addMessage = 'No LLM message found') {
|
|
83
|
+
if (messages.length === 0) {
|
|
84
|
+
PromptAssert.debug(this._prompts, true);
|
|
85
|
+
assert.fail(ex(`No LLM message ${addMessage}`, search));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const found = messages.some((m) => asserts.llmContains(m, search, false));
|
|
89
|
+
// console.log({ found, messages, notContains });
|
|
90
|
+
if (notContains === found) {
|
|
91
|
+
PromptAssert.debug(this._prompts, true);
|
|
92
|
+
assert.fail(ex(
|
|
93
|
+
`Text${notContains ? '' : ' not'} found in LLM messages ${addMessage}`,
|
|
94
|
+
search
|
|
95
|
+
));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* @param {LLMMessage[]} prompt
|
|
102
|
+
* @returns {string}
|
|
103
|
+
*/
|
|
104
|
+
static promptStats (prompt) {
|
|
105
|
+
const stats = prompt.reduce((o, m) => Object.assign(o, {
|
|
106
|
+
[m.role]: (o[m.role] || 0) + 1
|
|
107
|
+
}), {
|
|
108
|
+
system: 0, assistant: 0, user: 0, tool: 0
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return `[ ${Object.entries(stats).map(([k, v]) => `${k.toUpperCase()}: ${v}`).join(' ')} ]`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {LLMRole} role
|
|
117
|
+
* @return {string}
|
|
118
|
+
*/
|
|
119
|
+
static _marker (role) {
|
|
120
|
+
switch (role) {
|
|
121
|
+
case 'user':
|
|
122
|
+
return '>';
|
|
123
|
+
case 'system':
|
|
124
|
+
return '*';
|
|
125
|
+
case 'assistant':
|
|
126
|
+
return '<';
|
|
127
|
+
default:
|
|
128
|
+
return '-';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static _content (msg, full) {
|
|
133
|
+
if (typeof msg.content !== 'string') {
|
|
134
|
+
return msg.content === null ? '<null>' : typeof msg.content;
|
|
135
|
+
}
|
|
136
|
+
const txt = msg.content.replace(/\n+/g, ' ').replace(/\s\s+/g, ' ');
|
|
137
|
+
return `'${txt.length <= 100 || full ? txt : `${txt.substring(0, 100)}...`}'`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
*
|
|
142
|
+
* @param {PromptInfo[]} prompts
|
|
143
|
+
* @param {boolean} [full=false]
|
|
144
|
+
* @param {boolean} [silent=false]
|
|
145
|
+
* @returns {string[]}
|
|
146
|
+
*/
|
|
147
|
+
static debug (prompts, full = false, silent = false) {
|
|
148
|
+
let out;
|
|
149
|
+
if (prompts.length === 0) {
|
|
150
|
+
out = ['no LLM prompts occured'];
|
|
151
|
+
} else {
|
|
152
|
+
out = prompts.map((p, i) => {
|
|
153
|
+
const lastIndexOfUser = full
|
|
154
|
+
? 0
|
|
155
|
+
: p.prompt.reduce((li, m, c) => (m.role === 'user' ? c : li), 0);
|
|
156
|
+
|
|
157
|
+
const prompt = p.prompt.reduce((a, m, c) => {
|
|
158
|
+
if (['user', 'assistant'].includes(m.role) && c < lastIndexOfUser) {
|
|
159
|
+
const prev = a[a.length - 1];
|
|
160
|
+
if (prev === '...') {
|
|
161
|
+
return a;
|
|
162
|
+
}
|
|
163
|
+
return [...a, '...'];
|
|
164
|
+
}
|
|
165
|
+
return [...a, `${PromptAssert._marker(m.role)} ${PromptAssert._content(m, full)}`];
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
return [
|
|
169
|
+
`${i + 1}) ${PromptAssert.promptStats(p.prompt)}`,
|
|
170
|
+
`${prompt.join('\n ')}`,
|
|
171
|
+
`# ${PromptAssert._content(p.result)}`
|
|
172
|
+
].join('\n ');
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (!silent) {
|
|
176
|
+
// eslint-disable-next-line no-console
|
|
177
|
+
console.log(...out);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = PromptAssert;
|
|
File without changes
|