wingbot 3.73.0 → 3.73.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/package.json +4 -2
- package/src/BotAppSender.js +6 -4
- package/src/LLM.js +8 -2
- package/src/LLMSession.js +5 -3
- package/src/graphApi/GraphApi.js +31 -1
- package/src/graphApi/gqlRules.js +87 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wingbot",
|
|
3
|
-
"version": "3.73.
|
|
3
|
+
"version": "3.73.1",
|
|
4
4
|
"description": "Enterprise Messaging Bot Conversation Engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -58,7 +58,9 @@
|
|
|
58
58
|
"compress-json": "^3.0.0",
|
|
59
59
|
"deep-extend": "^0.6.0",
|
|
60
60
|
"form-data": "^4.0.0",
|
|
61
|
-
"graphql": "^16.
|
|
61
|
+
"graphql": "^16.11.0",
|
|
62
|
+
"graphql-depth-limit": "^1.1.0",
|
|
63
|
+
"graphql-query-complexity": "^1.1.0",
|
|
62
64
|
"jsonwebtoken": "^9.0.2",
|
|
63
65
|
"node-fetch": "^2.6.7",
|
|
64
66
|
"path-to-regexp": "^6.3.0",
|
package/src/BotAppSender.js
CHANGED
|
@@ -41,12 +41,12 @@ class BotAppSender extends ReturnSender {
|
|
|
41
41
|
/**
|
|
42
42
|
*
|
|
43
43
|
* @param {SenderOptions} options
|
|
44
|
-
* @param {string}
|
|
44
|
+
* @param {string} senderId
|
|
45
45
|
* @param {object} incommingMessage
|
|
46
46
|
* @param {ChatLogStorage} logger - console like logger
|
|
47
47
|
*/
|
|
48
|
-
constructor (options,
|
|
49
|
-
super(options,
|
|
48
|
+
constructor (options, senderId, incommingMessage, logger = null) {
|
|
49
|
+
super(options, senderId, incommingMessage, logger);
|
|
50
50
|
|
|
51
51
|
this.waits = true;
|
|
52
52
|
|
|
@@ -98,14 +98,16 @@ class BotAppSender extends ReturnSender {
|
|
|
98
98
|
* @param {Buffer} data
|
|
99
99
|
* @param {string} contentType
|
|
100
100
|
* @param {string} fileName
|
|
101
|
+
* @param {string} [senderId]
|
|
101
102
|
* @returns {Promise<UploadResult>}
|
|
102
103
|
*/
|
|
103
|
-
async upload (data, contentType, fileName) {
|
|
104
|
+
async upload (data, contentType, fileName, senderId = this._senderId) {
|
|
104
105
|
const formData = new FormData();
|
|
105
106
|
|
|
106
107
|
const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36).padEnd(11, '0');
|
|
107
108
|
|
|
108
109
|
formData.append('nonce', nonce);
|
|
110
|
+
formData.append('senderId', senderId || '');
|
|
109
111
|
formData.append('f0', data, { filename: fileName, contentType });
|
|
110
112
|
|
|
111
113
|
const [token, agent] = await Promise.all([
|
package/src/LLM.js
CHANGED
|
@@ -82,6 +82,11 @@ const Ai = require('./Ai');
|
|
|
82
82
|
* @prop {string} [model]
|
|
83
83
|
*/
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* @typedef {object} LLMLogOptions
|
|
87
|
+
* @prop {VectorSearchResult} [vectorSearchResult]
|
|
88
|
+
*/
|
|
89
|
+
|
|
85
90
|
/**
|
|
86
91
|
* @typedef {object} LLMChatProvider
|
|
87
92
|
* @prop {LLMChatProviderPrompt} requestChat
|
|
@@ -237,9 +242,10 @@ class LLM {
|
|
|
237
242
|
*
|
|
238
243
|
* @param {LLMSession} session
|
|
239
244
|
* @param {LLMProviderOptions} [options={}]
|
|
245
|
+
* @param {LLMLogOptions} [logOptions]
|
|
240
246
|
* @returns {Promise<LLMMessage>}
|
|
241
247
|
*/
|
|
242
|
-
async generate (session, options = {}) {
|
|
248
|
+
async generate (session, options = {}, logOptions = {}) {
|
|
243
249
|
/** @type {LLMProviderOptions} */
|
|
244
250
|
const opts = {
|
|
245
251
|
...(this._configuration.model && { model: this._configuration.model }),
|
|
@@ -248,7 +254,7 @@ class LLM {
|
|
|
248
254
|
|
|
249
255
|
const prompt = session.toArray(true);
|
|
250
256
|
const result = await this._provider.requestChat(prompt, opts);
|
|
251
|
-
this.logPrompt(prompt, result);
|
|
257
|
+
this.logPrompt(prompt, result, logOptions.vectorSearchResult);
|
|
252
258
|
return result;
|
|
253
259
|
}
|
|
254
260
|
|
package/src/LLMSession.js
CHANGED
|
@@ -7,6 +7,7 @@ const LLM = require('./LLM');
|
|
|
7
7
|
|
|
8
8
|
/** @typedef {import('./Responder').QuickReply} QuickReply */
|
|
9
9
|
/** @typedef {import('./LLM').LLMProviderOptions} LLMProviderOptions */
|
|
10
|
+
/** @typedef {import('./LLM').LLMLogOptions} LLMLogOptions */
|
|
10
11
|
|
|
11
12
|
/** @typedef {'user'|'assistant'} LLMChatRole */
|
|
12
13
|
/** @typedef {'system'} LLMSystemRole */
|
|
@@ -272,11 +273,12 @@ class LLMSession {
|
|
|
272
273
|
|
|
273
274
|
/**
|
|
274
275
|
*
|
|
275
|
-
* @param {LLMProviderOptions} [
|
|
276
|
+
* @param {LLMProviderOptions} [providerOptions={}]
|
|
277
|
+
* @param {LLMLogOptions} [logOptions]
|
|
276
278
|
* @returns {Promise<LLMMessage<any>>}
|
|
277
279
|
*/
|
|
278
|
-
async generate (
|
|
279
|
-
const result = await this._llm.generate(this,
|
|
280
|
+
async generate (providerOptions = {}, logOptions = {}) {
|
|
281
|
+
const result = await this._llm.generate(this, providerOptions, logOptions);
|
|
280
282
|
|
|
281
283
|
this._generatedIndex = this._chat.length;
|
|
282
284
|
this._chat.push(result);
|
package/src/graphApi/GraphApi.js
CHANGED
|
@@ -8,6 +8,7 @@ const WingbotApiConnector = require('./WingbotApiConnector');
|
|
|
8
8
|
// @ts-ignore
|
|
9
9
|
const packageJson = require('../../package.json');
|
|
10
10
|
const headersToAuditMeta = require('../utils/headersToAuditMeta');
|
|
11
|
+
const gqlRules = require('./gqlRules');
|
|
11
12
|
|
|
12
13
|
const DEFAULT_GROUPS = ['botEditor', 'botAdmin', 'appToken'];
|
|
13
14
|
const KEYS_URL = 'https://api.wingbot.ai/keys';
|
|
@@ -27,6 +28,12 @@ const DEFAULT_CACHE = 86400000; // 24 hours
|
|
|
27
28
|
/** @typedef {import('../CallbackAuditLog')} AuditLog */
|
|
28
29
|
/** @typedef {import('graphql')} GqlLib */
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {object} Logger
|
|
33
|
+
* @prop {Function} log
|
|
34
|
+
* @prop {Function} error
|
|
35
|
+
*/
|
|
36
|
+
|
|
30
37
|
class GraphApi {
|
|
31
38
|
|
|
32
39
|
/**
|
|
@@ -38,8 +45,11 @@ class GraphApi {
|
|
|
38
45
|
* @param {string[]} [options.groups] - list of allowed bot groups
|
|
39
46
|
* @param {boolean} [options.useBundledGql] - uses library bundled graphql definition
|
|
40
47
|
* @param {AuditLog} [options.auditLog]
|
|
48
|
+
* @param {boolean} [options.isProduction]
|
|
49
|
+
* @param {boolean} [options.hideVerboseErrors]
|
|
50
|
+
* @param {Logger} [log=console]
|
|
41
51
|
*/
|
|
42
|
-
constructor (apis, options) {
|
|
52
|
+
constructor (apis, options, log = console) {
|
|
43
53
|
this._root = {
|
|
44
54
|
version () {
|
|
45
55
|
return packageJson.version;
|
|
@@ -62,6 +72,13 @@ class GraphApi {
|
|
|
62
72
|
}
|
|
63
73
|
};
|
|
64
74
|
|
|
75
|
+
this._log = log;
|
|
76
|
+
this._options = {
|
|
77
|
+
hideVerboseErrors: true,
|
|
78
|
+
isProduction: true,
|
|
79
|
+
...options
|
|
80
|
+
};
|
|
81
|
+
|
|
65
82
|
Object.assign(opts, options);
|
|
66
83
|
|
|
67
84
|
apis.forEach((api) => Object.assign(this._root, api));
|
|
@@ -149,6 +166,19 @@ class GraphApi {
|
|
|
149
166
|
|
|
150
167
|
const schema = await this._schema();
|
|
151
168
|
|
|
169
|
+
const { isProduction = true, hideVerboseErrors } = this._options;
|
|
170
|
+
const ast = this._gql.parse(body.query);
|
|
171
|
+
const errors = this._gql.validate(
|
|
172
|
+
schema,
|
|
173
|
+
ast,
|
|
174
|
+
gqlRules(body.variables, isProduction, hideVerboseErrors, this._log)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (errors.length > 0) {
|
|
178
|
+
this._log.error('GQL failed', errors);
|
|
179
|
+
return { errors };
|
|
180
|
+
}
|
|
181
|
+
|
|
152
182
|
const ctx = {
|
|
153
183
|
token,
|
|
154
184
|
groups: this._defaultGroups,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable global-require */
|
|
2
|
+
/**
|
|
3
|
+
* @author David Menger
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
/** @typedef {import('graphql').ValidationRule} ValidationRule */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} Logger
|
|
11
|
+
* @prop {Function} log
|
|
12
|
+
* @prop {Function} error
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {object} variables
|
|
18
|
+
* @param {boolean} isProduction
|
|
19
|
+
* @param {boolean} hideVerboseErrors
|
|
20
|
+
* @param {Logger} [log=console]
|
|
21
|
+
* @returns {ValidationRule[]}
|
|
22
|
+
*/
|
|
23
|
+
function gqlRules (variables, isProduction, hideVerboseErrors, log = console) {
|
|
24
|
+
// OPTIMIZATION FOR LAMBDA PERFORMANCE
|
|
25
|
+
const { GraphQLError, NoSchemaIntrospectionCustomRule } = require('graphql');
|
|
26
|
+
const { createComplexityRule, simpleEstimator } = require('graphql-query-complexity');
|
|
27
|
+
const depthLimit = require('graphql-depth-limit');
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
...(hideVerboseErrors ? [NoSchemaIntrospectionCustomRule] : []),
|
|
31
|
+
depthLimit(10),
|
|
32
|
+
createComplexityRule({
|
|
33
|
+
// The maximum allowed query complexity, queries above this threshold will be rejected
|
|
34
|
+
maximumComplexity: 1000,
|
|
35
|
+
|
|
36
|
+
// The query variables. This is needed because the variables are not available
|
|
37
|
+
// in the visitor of the graphql-js library
|
|
38
|
+
variables,
|
|
39
|
+
|
|
40
|
+
// The context object for the request (optional)
|
|
41
|
+
context: {},
|
|
42
|
+
|
|
43
|
+
// The maximum number of query nodes to evaluate (fields, fragments, composite types).
|
|
44
|
+
// If a query contains more than the specified number of nodes, the complexity rule will
|
|
45
|
+
// throw an error, regardless of the complexity of the query.
|
|
46
|
+
//
|
|
47
|
+
// Default: 10_000
|
|
48
|
+
maxQueryNodes: 10000,
|
|
49
|
+
|
|
50
|
+
// Optional callback function to retrieve the determined query complexity
|
|
51
|
+
// Will be invoked whether the query is rejected or not
|
|
52
|
+
// This can be used for logging or to implement rate limiting
|
|
53
|
+
onComplete: (complexity) => {
|
|
54
|
+
if (!isProduction) {
|
|
55
|
+
log.log('Determined query complexity: ', complexity);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// Optional function to create a custom error
|
|
60
|
+
createError: (max, actual) => {
|
|
61
|
+
const msg = `GQL Query too complex: ${actual}. Maximum allowed: ${max}`;
|
|
62
|
+
|
|
63
|
+
if (hideVerboseErrors) {
|
|
64
|
+
log.error(msg);
|
|
65
|
+
return new GraphQLError('');
|
|
66
|
+
}
|
|
67
|
+
log.log(msg);
|
|
68
|
+
return new GraphQLError(`Query is too complex: ${actual}. Maximum allowed complexity: ${max}`);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Add any number of estimators. The estimators are invoked in order, the first
|
|
72
|
+
// numeric value that is being returned by an estimator is used as the field complexity.
|
|
73
|
+
// If no estimator returns a value, an exception is raised.
|
|
74
|
+
estimators: [
|
|
75
|
+
// Add more estimators here...
|
|
76
|
+
|
|
77
|
+
// This will assign each field a complexity of 1 if no other estimator
|
|
78
|
+
// returned a value.
|
|
79
|
+
simpleEstimator({
|
|
80
|
+
defaultComplexity: 1
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
})
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = gqlRules;
|