@tiledesk/tiledesk-tybot-connector 2.0.21-rc9 → 2.0.22

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.
@@ -11,10 +11,6 @@ const winston = require('../../utils/winston');
11
11
  const httpUtils = require("../../utils/HttpUtils");
12
12
  const integrationService = require("../../services/IntegrationService");
13
13
  const { Logger } = require("../../Logger");
14
- const quotasService = require("../../services/QuotasService");
15
- const llmService = require("../../services/LLMService");
16
-
17
-
18
14
 
19
15
  class DirAskGPTV2 {
20
16
 
@@ -169,14 +165,11 @@ class DirAskGPTV2 {
169
165
  }
170
166
  }
171
167
 
172
- const kb_endpoint = process.env.KB_ENDPOINT_QA
173
- winston.verbose("DirAskGPTV2 KbEndpoint URL: " + kb_endpoint);
174
-
175
168
  let key = await integrationService.getKeyFromIntegrations(this.projectId, 'openai', this.token);
176
169
  if (!key) {
177
170
  this.logger.native("[Ask Knowledge Base] OpenAI key not found in Integration. Using shared OpenAI key");
178
171
  winston.verbose("DirAskGPTV2 - Key not found in Integrations. Searching in kb settings...");
179
- key = await llmService.getKeyFromKbSettings(this.projectId, this.token);
172
+ key = await this.getKeyFromKbSettings();
180
173
  }
181
174
 
182
175
  if (!key) {
@@ -200,7 +193,7 @@ class DirAskGPTV2 {
200
193
  }
201
194
 
202
195
  if (publicKey === true && !chunks_only) {
203
- let keep_going = await quotasService.checkQuoteAvailability(this.projectId, this.token);
196
+ let keep_going = await this.checkQuoteAvailability();
204
197
  if (keep_going === false) {
205
198
  this.logger.warn("[Ask Knowledge Base] Tokens quota exceeded. Skip the action")
206
199
  winston.verbose("DirAskGPTV2 - Quota exceeded for tokens. Skip the action")
@@ -299,6 +292,12 @@ class DirAskGPTV2 {
299
292
 
300
293
  winston.debug("DirAskGPTV2 json:", json);
301
294
 
295
+ let kb_endpoint = process.env.KB_ENDPOINT_QA;
296
+ if (ns.hybrid === true) {
297
+ kb_endpoint = process.env.KB_ENDPOINT_QA_GPU;
298
+ }
299
+ winston.verbose("DirAskGPTV2 KbEndpoint URL: " + kb_endpoint);
300
+
302
301
  const HTTPREQUEST = {
303
302
  url: kb_endpoint + "/qa",
304
303
  headers: {
@@ -345,9 +344,7 @@ class DirAskGPTV2 {
345
344
  tokens: resbody.prompt_token_size,
346
345
  model: json.model
347
346
  }
348
- quotasService.updateQuote(this.projectId, this.token, tokens_usage).catch((err) => {
349
- winston.error("Error updating quota: ", err);
350
- })
347
+ this.updateQuote(tokens_usage);
351
348
  }
352
349
 
353
350
  if (trueIntent) {
@@ -360,10 +357,6 @@ class DirAskGPTV2 {
360
357
  }
361
358
  } else {
362
359
  await this.#assignAttributes(action, answer, source);
363
- llmService.addUnansweredQuestion(this.projectId, json.namespace, json.question, this.token).catch((err) => {
364
- winston.error("DirAskGPTV2 - Error adding unanswered question: ", err);
365
- this.logger.warn("[Ask Knowledge Base] Unable to add unanswered question", json.question, "to namespacae", json.namespace);
366
- })
367
360
  if (falseIntent) {
368
361
  await this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes);
369
362
  callback(true);
@@ -434,6 +427,93 @@ class DirAskGPTV2 {
434
427
  }
435
428
  }
436
429
 
430
+ async getKeyFromKbSettings() {
431
+ return new Promise((resolve) => {
432
+
433
+ const KB_HTTPREQUEST = {
434
+ url: this.API_ENDPOINT + "/" + this.context.projectId + "/kbsettings",
435
+ headers: {
436
+ 'Content-Type': 'application/json',
437
+ 'Authorization': 'JWT ' + this.context.token
438
+ },
439
+ method: "GET"
440
+ }
441
+ winston.debug("DirAskGPTV2 KB HttpRequest", KB_HTTPREQUEST);
442
+
443
+ httpUtils.request(
444
+ KB_HTTPREQUEST, async (err, resbody) => {
445
+ if (err) {
446
+ winston.error("DirAskGPTV2 Get kb settings error ", err?.response?.data);
447
+ resolve(null);
448
+ } else {
449
+ if (!resbody.gptkey) {
450
+ resolve(null);
451
+ } else {
452
+ resolve(resbody.gptkey);
453
+ }
454
+ }
455
+ }
456
+ )
457
+ })
458
+ }
459
+
460
+ async checkQuoteAvailability() {
461
+ return new Promise((resolve) => {
462
+
463
+ const HTTPREQUEST = {
464
+ url: this.API_ENDPOINT + "/" + this.context.projectId + "/quotes/tokens",
465
+ headers: {
466
+ 'Content-Type': 'application/json',
467
+ 'Authorization': 'JWT ' + this.context.token
468
+ },
469
+ method: "GET"
470
+ }
471
+ winston.debug("DirAskGPTV2 check quote availability HttpRequest", HTTPREQUEST);
472
+
473
+ httpUtils.request(
474
+ HTTPREQUEST, async (err, resbody) => {
475
+ if (err) {
476
+ winston.error("DirAskGPTV2 Check quote availability err: ", err);
477
+ resolve(true)
478
+ } else {
479
+ if (resbody.isAvailable === true) {
480
+ resolve(true)
481
+ } else {
482
+ resolve(false)
483
+ }
484
+ }
485
+ }
486
+ )
487
+ })
488
+ }
489
+
490
+ async updateQuote(tokens_usage) {
491
+ return new Promise((resolve, reject) => {
492
+
493
+ const HTTPREQUEST = {
494
+ url: this.API_ENDPOINT + "/" + this.context.projectId + "/quotes/incr/tokens",
495
+ headers: {
496
+ 'Content-Type': 'application/json',
497
+ 'Authorization': 'JWT ' + this.context.token
498
+ },
499
+ json: tokens_usage,
500
+ method: "POST"
501
+ }
502
+ winston.debug("DirAskGPTV2 update quote HttpRequest ", HTTPREQUEST);
503
+
504
+ httpUtils.request(
505
+ HTTPREQUEST, async (err, resbody) => {
506
+ if (err) {
507
+ winston.error("DirAskGPTV2 Increment tokens quote err: ", err);
508
+ reject(false)
509
+ } else {
510
+ resolve(true);
511
+ }
512
+ }
513
+ )
514
+ })
515
+ }
516
+
437
517
  /**
438
518
  * Transforms the transcirpt array in a dictionary like '0': { "question": "xxx", "answer":"xxx"}
439
519
  * merging consecutive messages with the same role in a single question or answer.
@@ -522,7 +602,7 @@ class DirAskGPTV2 {
522
602
  type: isHybrid ? "serverless" : "pod",
523
603
  apikey: "",
524
604
  vector_size: 1536,
525
- index_name: isHybrid ? "hybrid_index" : "standard_index"
605
+ index_name: isHybrid ? process.env.PINECONE_INDEX_HYBRID : process.env.PINECONE_INDEX
526
606
  }
527
607
  resolve(engine);
528
608
  })
@@ -75,7 +75,6 @@ class DirReplyV2 {
75
75
  // }
76
76
 
77
77
  TiledeskChatbotUtil.replaceJSONButtons(message, requestAttributes);
78
- TiledeskChatbotUtil.replaceJSONGalleries(message, requestAttributes);
79
78
 
80
79
  try {
81
80
  // lock/unlock + no-match
@@ -118,7 +118,7 @@ class DirWebRequestV2 {
118
118
  this.logger.native("[Web Request] resbody: ", resbody);
119
119
 
120
120
  if (err) {
121
- this.logger.error("[Web Request] error: ", err);
121
+ this.logger.error("WebRequest error: ", err);
122
122
  winston.log("webRequest error: ", err);
123
123
  if (callback) {
124
124
  if (falseIntent) {
@@ -0,0 +1,418 @@
1
+ let axios = require('axios');
2
+ let https = require("https");
3
+ const { Filler } = require('../Filler');
4
+ const { TiledeskChatbot } = require('../../engine/TiledeskChatbot');
5
+ const { DirIntent } = require('./DirIntent');
6
+
7
+ class DirWebRequestV2 {
8
+ constructor(context) {
9
+ if (!context) {
10
+ throw new Error('context object is mandatory.');
11
+ }
12
+ this.context = context;
13
+ this.tdcache = context.tdcache;
14
+ this.requestId = context.requestId;
15
+ this.chatbot = context.chatbot;
16
+ this.log = context.log;
17
+
18
+ this.intentDir = new DirIntent(context);
19
+ }
20
+
21
+ execute(directive, callback) {
22
+ let action;
23
+ if (directive.action) {
24
+ action = directive.action;
25
+ }
26
+ else {
27
+ console.error("Incorrect directive:", JSON.stringify(directive));
28
+ callback();
29
+ return;
30
+ }
31
+ this.go(action, (stop) => {
32
+ if (this.log) {console.log("(webrequestv2, stop?", stop); }
33
+ callback(stop);
34
+ });
35
+ }
36
+
37
+ async go(action, callback) {
38
+ if (this.log) {console.log("webRequest action:", JSON.stringify(action));}
39
+
40
+ // Condition branches
41
+ let trueIntent = action.trueIntent;
42
+ let falseIntent = action.falseIntent;
43
+ const trueIntentAttributes = action.trueIntentAttributes;
44
+ const falseIntentAttributes = action.falseIntentAttributes;
45
+ let stopOnConditionMet = action.stopOnConditionMet;
46
+ if (trueIntent && trueIntent.trim() === "") {
47
+ trueIntent = null;
48
+ }
49
+ if (falseIntent && falseIntent.trim() === "") {
50
+ falseIntent = null;
51
+ }
52
+
53
+ let requestAttributes = null;
54
+ if (this.tdcache) {
55
+ requestAttributes =
56
+ await TiledeskChatbot.allParametersStatic(
57
+ this.tdcache, this.requestId
58
+ );
59
+ }
60
+ const filler = new Filler();
61
+ const url = filler.fill(action.url, requestAttributes);
62
+
63
+ let headers = {};
64
+ if (action.headersString) {
65
+ let headersDict = action.headersString
66
+ for (const [key, value] of Object.entries(headersDict)) {
67
+ if (this.log) {console.log("header:", key, "value:", value)}
68
+ let filled_value = filler.fill(value, requestAttributes);
69
+ headers[key] = filled_value;
70
+ }
71
+ }
72
+
73
+ let json = null;
74
+ try {
75
+ if (action.jsonBody && action.bodyType == "json") {
76
+ if (this.log) {console.log("action.body is:", action.jsonBody);}
77
+ let jsonBody = filler.fill(action.jsonBody, requestAttributes);
78
+ try {
79
+ json = JSON.parse(jsonBody);
80
+ if (this.log) {console.log("json is:", json);}
81
+ }
82
+ catch(err) {
83
+ console.error("Error parsing webRequest jsonBody:", jsonBody);
84
+ if (callback) {
85
+ if (falseIntent) {
86
+ await this.chatbot.addParameter("flowError", "Error parsing jsonBody");
87
+ this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
88
+ console.log('herrrrr 11111' )
89
+ callback(true); // stop the flow
90
+ return;
91
+ });
92
+ }
93
+ else {
94
+ console.log('herrrrr 2222' )
95
+ callback(false); // continue the flow
96
+ return;
97
+ }
98
+ }
99
+ }
100
+ }
101
+ else if (action.formData && action.bodyType == "form-data") {
102
+ let formData = filler.fill(action.formData, requestAttributes);
103
+ if (this.log) {console.log("action.body is form-data:", formData);}
104
+ // // fill
105
+ if (formData && formData.length > 0) {
106
+ for (let i = 0; i < formData.length; i++) {
107
+ let field = formData[i];
108
+ if (field.value) {
109
+ field.value = filler.fill(field.value, requestAttributes);
110
+ if (this.log) {console.log("field filled:", field.value);}
111
+ }
112
+ }
113
+ }
114
+ json = {};
115
+ for (let i = 0; i < formData.length; i++) {
116
+ let field = formData[i];
117
+ if (field.enabled && field.value && field.type === "URL") {
118
+ if (this.log) {console.log("Getting file:", field.value);}
119
+ let response = await axios.get(field.value,
120
+ {
121
+ responseType: 'stream'
122
+ }
123
+ );
124
+ let stream = response.data;
125
+ // if (this.log) {console.log("Stream data:", stream);}
126
+ json[field.name] = stream;
127
+ // process.exit(0);
128
+ }
129
+ else if (field.enabled && field.value && field.type === "Text") {
130
+ json[field.name] = field.value;
131
+ }
132
+ }
133
+ if (this.log) {console.log("final json:", json);}
134
+ }
135
+ else {
136
+ if (this.log) {console.log("no action upload parts");}
137
+ }
138
+
139
+ }
140
+ catch(error) {
141
+ console.error("Error", error);
142
+ if (callback) {
143
+ if (falseIntent) {
144
+ await this.chatbot.addParameter("flowError", "Error: " + error);
145
+ this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
146
+ callback(true); // stop the flow
147
+ return;
148
+ });
149
+ }
150
+ else {
151
+ callback(false); // continue the flow
152
+ return;
153
+ }
154
+ }
155
+ }
156
+
157
+
158
+
159
+ let timeout = this.#webrequest_timeout(action, 20000, 1, 300000);
160
+
161
+ if (this.log) {console.log("webRequest URL", url);}
162
+
163
+ const HTTPREQUEST = {
164
+ url: url,
165
+ headers: headers,
166
+ json: json,
167
+ method: action.method,
168
+ timeout: timeout
169
+ };
170
+
171
+ if (this.log) {console.log("webRequest HTTPREQUEST", HTTPREQUEST);}
172
+ this.#myrequest(
173
+ HTTPREQUEST, async (err, res) => {
174
+ if (this.log && err) {
175
+ console.log("webRequest error:", err);
176
+ }
177
+ if (this.log) {console.log("got res:", res);}
178
+ let resbody = res.data;
179
+ let status = res.status;
180
+ let error = res.error;
181
+ await this.#assignAttributes(action, resbody, status, error)
182
+ if (this.log) {console.log("webRequest resbody:", resbody);}
183
+ if (err) {
184
+ if (this.log) {console.error("webRequest error:", err);}
185
+ if (callback) {
186
+ if (falseIntent) {
187
+ this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
188
+ callback(true); // stop the flow
189
+ });
190
+ }
191
+ else {
192
+ callback(false); // continue the flow
193
+ }
194
+ }
195
+ }
196
+ else if(res.status >= 200 && res.status <= 299) {
197
+ if (trueIntent) {
198
+ await this.#executeCondition(true, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
199
+ callback(true); // stop the flow
200
+ });
201
+ }
202
+ else {
203
+ callback(false); // continue the flow
204
+ }
205
+ }
206
+ else {
207
+ if (falseIntent) {
208
+ this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, () => {
209
+ callback(true); // stop the flow
210
+ });
211
+ }
212
+ else {
213
+ callback(false); // continue the flow
214
+ }
215
+ }
216
+ }
217
+ );
218
+ }
219
+
220
+ async #executeCondition(result, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes, callback) {
221
+ let trueIntentDirective = null;
222
+ if (trueIntent) {
223
+ trueIntentDirective = DirIntent.intentDirectiveFor(trueIntent, trueIntentAttributes);
224
+ }
225
+ let falseIntentDirective = null;
226
+ if (falseIntent) {
227
+ falseIntentDirective = DirIntent.intentDirectiveFor(falseIntent, falseIntentAttributes);
228
+ }
229
+ if (result === true) {
230
+ if (trueIntentDirective) {
231
+ this.intentDir.execute(trueIntentDirective, () => {
232
+ callback();
233
+ });
234
+ }
235
+ else {
236
+ if (this.log) {console.log("No trueIntentDirective specified");}
237
+ callback();
238
+ }
239
+ }
240
+ else {
241
+ if (falseIntentDirective) {
242
+ this.intentDir.execute(falseIntentDirective, () => {
243
+ callback();
244
+ });
245
+ }
246
+ else {
247
+ if (this.log) {console.log("No falseIntentDirective specified");}
248
+ callback();
249
+ }
250
+ }
251
+ }
252
+
253
+ async #assignAttributes(action, resbody, status, error) {
254
+ if (this.log) {
255
+ console.log("assignAttributes resbody:", resbody)
256
+ console.log("assignAttributes error:", error)
257
+ console.log("assignAttributes status:", status)
258
+ console.log("assignAttributes action:", action)
259
+ }
260
+ if (this.context.tdcache) {
261
+ if (action.assignResultTo && resbody) {
262
+ if (this.log) {console.log("assign assignResultTo:", resbody);}
263
+ await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, action.assignResultTo, resbody);
264
+ }
265
+ if (action.assignErrorTo && error) {
266
+ if (this.log) {console.log("assign assignResultTo:", error);}
267
+ await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, action.assignErrorTo, error);
268
+ }
269
+ if (action.assignStatusTo && status) {
270
+ if (this.log) {console.log("assign assignStatusTo:", status);}
271
+ await TiledeskChatbot.addParameterStatic(this.context.tdcache, this.context.requestId, action.assignStatusTo, status);
272
+ }
273
+ // Debug log
274
+ if (this.log) {
275
+ const all_parameters = await TiledeskChatbot.allParametersStatic(this.context.tdcache, this.context.requestId);
276
+ for (const [key, value] of Object.entries(all_parameters)) {
277
+ if (this.log) {console.log("(webRequest) request parameter:", key, "value:", value, "type:", typeof value)}
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ #myrequest(options, callback) {
284
+ try {
285
+ if (this.log) {
286
+ console.log("API URL:", options.url);
287
+ //console.log("** Options:", JSON.stringify(options));
288
+ // Stringify "options". FIX THE STRINGIFY OF CIRCULAR STRUCTURE BUG - START
289
+ let cache = [];
290
+ let str_Options = JSON.stringify(options, function(key, value) { // try to use a separate function
291
+ if (typeof value === 'object' && value != null) {
292
+ if (cache.indexOf(value) !== -1) {
293
+ return;
294
+ }
295
+ cache.push(value);
296
+ }
297
+ return value;
298
+ });
299
+ console.log("** Options:", str_Options);
300
+ }
301
+ let axios_options = {
302
+ url: options.url,
303
+ method: options.method,
304
+ params: options.params,
305
+ headers: options.headers,
306
+ timeout: options.timeout,
307
+ maxContentLength: 10000000, // max 10mb response size
308
+ maxBodyLength: 10000000 // max 10mb request body size
309
+ }
310
+
311
+ if (options.json !== null) {
312
+ axios_options.data = options.json
313
+ }
314
+ // if (this.log) {
315
+ // console.log("axios_options:", JSON.stringify(axios_options));
316
+ // }
317
+ if (options.url.startsWith("https:")) {
318
+ const httpsAgent = new https.Agent({
319
+ rejectUnauthorized: false,
320
+ });
321
+ axios_options.httpsAgent = httpsAgent;
322
+ }
323
+
324
+ axios(axios_options)
325
+ .then((res) => {
326
+ if (this.log) {
327
+ console.log("Success Response:", res);
328
+ console.log("Response for url:", options.url);
329
+ console.log("Response headers:\n", JSON.stringify(res.headers));
330
+ }
331
+ if (callback) {
332
+ callback(null, res);
333
+ }
334
+ })
335
+ .catch( (err) => {
336
+ if (this.log) {
337
+ if (err.response) {
338
+ console.log("Error Response data:", err.response.data);
339
+ }
340
+ // FIX THE STRINGIFY OF CIRCULAR STRUCTURE BUG - START
341
+ let cache = [];
342
+ let error_log = JSON.stringify(err, function(key, value) { // try to use a separate function
343
+ if (typeof value === 'object' && value != null) {
344
+ if (cache.indexOf(value) !== -1) {
345
+ return;
346
+ }
347
+ cache.push(value);
348
+ }
349
+ return value;
350
+ });
351
+ console.error("An error occurred: ", error_log);
352
+ // FIX THE STRINGIFY OF CIRCULAR STRUCTURE BUG - END
353
+ // console.error("An error occurred:", JSON.stringify(err));
354
+ }
355
+ if (callback) {
356
+ let status = 1000;
357
+ let cache = [];
358
+ let str_error = JSON.stringify(err, function(key, value) { // try to use a separate function
359
+ if (typeof value === 'object' && value != null) {
360
+ if (cache.indexOf(value) !== -1) {
361
+ return;
362
+ }
363
+ cache.push(value);
364
+ }
365
+ return value;
366
+ });
367
+ let error = JSON.parse(str_error) // "status" disappears without this trick
368
+ let errorMessage = JSON.stringify(error);
369
+ if (error.status) {
370
+ status = error.status;
371
+ }
372
+ if (error.message) {
373
+ errorMessage = error.message;
374
+ }
375
+ let data = null;
376
+ if (err.response) {
377
+ data = err.response.data;
378
+ }
379
+ callback(
380
+ null, {
381
+ status: status,
382
+ data: data,
383
+ error: errorMessage
384
+ }
385
+ );
386
+ }
387
+ });
388
+ }
389
+ catch(error) {
390
+ console.error("Error:", error);
391
+ }
392
+ }
393
+
394
+ #webrequest_timeout(action, default_timeout, min, max) {
395
+ let timeout = default_timeout;
396
+ if (!action.settings) {
397
+ return timeout;
398
+ }
399
+ // console.log("default timeout:", timeout);
400
+ // console.log("action.settings:", action.settings);
401
+ // console.log("action.settings.timeout:", action.settings.timeout);
402
+ // console.log("typeof action.settings.timeout:", typeof action.settings.timeout);
403
+ // console.log("action.settings.timeout > min", action.settings.timeout > min)
404
+ // console.log("action.settings.timeout < max", action.settings.timeout < max)
405
+
406
+ if (action.settings.timeout) {
407
+ if ((typeof action.settings.timeout === "number") && action.settings.timeout > min && action.settings.timeout < max) {
408
+ timeout = Math.round(action.settings.timeout)
409
+ // console.log("new timeout:", timeout);
410
+ }
411
+ }
412
+ // console.log("returning timeout:", timeout);
413
+ return timeout
414
+ }
415
+
416
+ }
417
+
418
+ module.exports = { DirWebRequestV2 };
@@ -58,7 +58,6 @@ class Directives {
58
58
  static MOVE_TO_UNASSIGNED = "move_to_unassigned";
59
59
  static CONNECT_BLOCK = "connect_block";
60
60
  static ADD_TAGS = 'add_tags'
61
- static WEBHOOK = 'webhook';
62
61
  static WEB_RESPONSE = "web_response";
63
62
  static FLOW_LOG = "flow_log";
64
63
  static ADD_KB_CONTENT = "add_kb_content";