@tiledesk/tiledesk-tybot-connector 2.0.20 → 2.0.21-rc10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-tybot-connector",
3
- "version": "2.0.20",
3
+ "version": "2.0.21-rc10",
4
4
  "description": "Tiledesk Tybot connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,105 @@
1
+ const httpUtils = require('../utils/HttpUtils');
2
+ const winston = require('../utils/winston');
3
+ const API_ENDPOINT = process.env.API_ENDPOINT;
4
+
5
+ class LLMService {
6
+
7
+ constructor() { }
8
+
9
+ async getNamespace(id_project, token, name, id) {
10
+ return new Promise((resolve) => {
11
+ const http_request = {
12
+ url: API_ENDPOINT + "/" + id_project + "/kb/namespace/all",
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'Authorization': 'JWT ' + token
16
+ },
17
+ method: "GET"
18
+ }
19
+ winston.debug("Kb HttpRequest", http_request);
20
+
21
+ httpUtils.request(
22
+ http_request, async (err, namespaces) => {
23
+ if (err) {
24
+ winston.error("Error getting namespaces:", err);
25
+ reject(err);
26
+ } else {
27
+ winston.debug("Get namespaces response:", namespaces);
28
+ if (!Array.isArray(namespaces)) {
29
+ reject(new Error('Invalid response format'));
30
+ return;
31
+ }
32
+
33
+ let namespace;
34
+ if (name) {
35
+ namespace = namespaces.find(n => n.name === name);
36
+ } else {
37
+ namespace = namespaces.find(n => n.id === id);
38
+ }
39
+ resolve(namespace || null);
40
+ }
41
+ }
42
+ )
43
+ })
44
+ }
45
+
46
+ async getKeyFromKbSettings(id_project, token) {
47
+
48
+ return new Promise((resolve) => {
49
+ const http_request = {
50
+ url: API_ENDPOINT + "/" + id_project + "/kbsettings",
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ 'Authorization': 'JWT ' + token
54
+ },
55
+ method: "GET"
56
+ }
57
+ winston.debug("Kb HttpRequest", http_request);
58
+
59
+ httpUtils.request(
60
+ http_request, async (err, resbody) => {
61
+ if (err) {
62
+ winston.error("Error getting kb settings:", err?.response?.data);
63
+ resolve(null);
64
+ } else {
65
+ if (!resbody || !resbody.gptkey) {
66
+ resolve(null);
67
+ } else {
68
+ resolve(resbody.gptkey);
69
+ }
70
+ }
71
+ }
72
+ )
73
+ })
74
+ }
75
+
76
+ async addUnansweredQuestion(id_project, namespace, question, token) {
77
+
78
+ const json = { namespace, question };
79
+
80
+ return new Promise((resolve, reject) => {
81
+ const http_request = {
82
+ url: API_ENDPOINT + "/" + id_project + "/kb/unanswered/",
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ 'Authorization': 'JWT ' + token
86
+ },
87
+ method: "POST",
88
+ json: json
89
+ }
90
+ winston.debug("Kb HttpRequest", http_request);
91
+
92
+ httpUtils.request(http_request, (err, response) => {
93
+ if (err) {
94
+ winston.error("Error adding unanswered question:", err);
95
+ reject(err);
96
+ } else {
97
+ resolve(response);
98
+ }
99
+ });
100
+ });
101
+ }
102
+ }
103
+
104
+ const llmService = new LLMService();
105
+ module.exports = llmService;
@@ -0,0 +1,69 @@
1
+ const httpUtils = require('../utils/HttpUtils');
2
+ const winston = require('../utils/winston');
3
+ const API_ENDPOINT = process.env.API_ENDPOINT;
4
+
5
+ class QuotasService {
6
+
7
+ constructor() { }
8
+
9
+ async checkQuoteAvailability(id_project, token) {
10
+ return new Promise((resolve) => {
11
+
12
+ const http_request = {
13
+ url: API_ENDPOINT + "/" + id_project + "/quotes/tokens",
14
+ headers: {
15
+ 'Content-Type': 'application/json',
16
+ 'Authorization': 'JWT ' + token
17
+ },
18
+ method: "GET"
19
+ }
20
+ winston.debug("QuotasService HttpRequest", http_request);
21
+
22
+ httpUtils.request(
23
+ http_request, async (err, resbody) => {
24
+ if (err) {
25
+ winston.error("Check quote availability err: ", err);
26
+ resolve(true)
27
+ } else {
28
+ if (resbody.isAvailable === true) {
29
+ resolve(true)
30
+ } else {
31
+ resolve(false)
32
+ }
33
+ }
34
+ }
35
+ )
36
+ })
37
+ }
38
+
39
+ async updateQuote(id_project, token, tokens_usage) {
40
+ return new Promise((resolve, reject) => {
41
+
42
+ const http_request = {
43
+ url: API_ENDPOINT + "/" + id_project + "/quotes/incr/tokens",
44
+ headers: {
45
+ 'Content-Type': 'application/json',
46
+ 'Authorization': 'JWT ' + token
47
+ },
48
+ json: tokens_usage,
49
+ method: "POST"
50
+ }
51
+ winston.debug("DirAskGPTV2 update quote HttpRequest ", http_request);
52
+
53
+ httpUtils.request(
54
+ http_request, async (err, resbody) => {
55
+ if (err) {
56
+ winston.error("Increment tokens quote err: ", err);
57
+ reject(false)
58
+ } else {
59
+ resolve(true);
60
+ }
61
+ }
62
+ )
63
+ })
64
+ }
65
+
66
+ }
67
+
68
+ const quotasService = new QuotasService();
69
+ module.exports = quotasService;
@@ -54,13 +54,13 @@ const { DirMoveToUnassigned } = require('./directives/DirMoveToUnassigned');
54
54
  const { DirAddTags } = require('./directives/DirAddTags');
55
55
  const { DirSendWhatsapp } = require('./directives/DirSendWhatsapp');
56
56
  const { DirReplaceBotV3 } = require('./directives/DirReplaceBotV3');
57
- const { DirAiTask, DirAiPrompt } = require('./directives/DirAiPrompt');
57
+ const { DirAiPrompt } = require('./directives/DirAiPrompt');
58
58
  const { DirWebResponse } = require('./directives/DirWebResponse');
59
59
  const { DirConnectBlock } = require('./directives/DirConnectBlock');
60
+ const { DirAddKbContent } = require('./directives/DirAddKbContent');
61
+ const { DirFlowLog } = require('./directives/DirFlowLog');
60
62
 
61
63
  const winston = require('../utils/winston');
62
- const { DirFlowLog } = require('./directives/DirFlowLog');
63
- const { DirAddKbContent } = require('./directives/DirAddKbContent');
64
64
 
65
65
  class DirectivesChatbotPlug {
66
66
 
@@ -219,14 +219,13 @@ class DirectivesChatbotPlug {
219
219
  directive_name = directive.name.toLowerCase();
220
220
  }
221
221
  if (directive && directive.action) {
222
- const action_id = directive.action["_tdActionId"];
223
- const locked_action_id = await this.chatbot.currentLockedAction(this.supportRequest.request_id);
224
- if ( locked_action_id && (locked_action_id !== action_id) ) {
225
- let next_dir = await this.nextDirective(this.directives);
226
- this.process(next_dir);
227
- return;
228
- }
229
-
222
+ const action_id = directive.action["_tdActionId"];
223
+ const locked_action_id = await this.chatbot.currentLockedAction(this.supportRequest.request_id);
224
+ if ( locked_action_id && (locked_action_id !== action_id) ) {
225
+ let next_dir = await this.nextDirective(this.directives);
226
+ this.process(next_dir);
227
+ return;
228
+ }
230
229
  }
231
230
  if (directive == null || (directive !== null && directive["name"] === undefined)) {
232
231
  winston.debug("(DirectivesChatbotPlug) stop process(). directive is (null?): ", directive);
@@ -704,6 +703,19 @@ class DirectivesChatbotPlug {
704
703
  }
705
704
  });
706
705
  }
706
+ else if (directive_name === Directives.WEBHOOK) {
707
+ // console.log(".....DirIntent")
708
+ new DirIntent(context).execute(directive, async (stop) => {
709
+ if (stop) {
710
+ if (context.log) { console.log("Stopping Actions on:", JSON.stringify(directive));}
711
+ this.theend();
712
+ }
713
+ else {
714
+ let next_dir = await this.nextDirective(this.directives);
715
+ this.process(next_dir);
716
+ }
717
+ });
718
+ }
707
719
  else if (directive_name === Directives.WEB_RESPONSE) {
708
720
  new DirWebResponse(context).execute(directive, async () => {
709
721
  let next_dir = await this.nextDirective(this.directives);
@@ -716,6 +728,12 @@ class DirectivesChatbotPlug {
716
728
  this.process(next_dir);
717
729
  })
718
730
  }
731
+ else if (directive_name === Directives.ADD_KB_CONTENT) {
732
+ new DirAddKbContent(context).execute(directive, async () => {
733
+ let next_dir = await this.nextDirective(this.directives);
734
+ this.process(next_dir);
735
+ });
736
+ }
719
737
  else {
720
738
  let next_dir = await this.nextDirective(this.directives);
721
739
  this.process(next_dir);
@@ -116,6 +116,14 @@ class DirAddKbContent {
116
116
  }
117
117
  }
118
118
 
119
+ if (!namespace) {
120
+ this.logger.error("[Add to KnwoledgeBase] Namespace is undefined")
121
+ winston.verbose("[DirAddKbContent] - Error: namespace is undefined")
122
+ await this.chatbot.addParameter("flowError", "[DirAddKbContent] Error: namespace is undefined");
123
+ callback(true);
124
+ return;
125
+ }
126
+
119
127
  let ns;
120
128
 
121
129
  if (action.namespaceAsName) {
@@ -137,20 +145,12 @@ class DirAddKbContent {
137
145
  return;
138
146
  }
139
147
 
140
- if (ns.engine) {
141
- engine = ns.engine;
142
- } else {
143
- engine = await this.setDefaultEngine()
144
- }
148
+ // if (ns.engine) {
149
+ // engine = ns.engine;
150
+ // } else {
151
+ // engine = await this.setDefaultEngine(ns.hybrid);
152
+ // }
145
153
 
146
- if (!namespace) {
147
- this.logger.error("[Add to KnwoledgeBase] Namespace is undefined")
148
- winston.verbose("[DirAddKbContent] - Error: namespace is undefined")
149
- await this.chatbot.addParameter("flowError", "[DirAddKbContent] Error: namespace is undefined");
150
- callback(true);
151
- return;
152
- }
153
-
154
154
  let json = {
155
155
  content: filled_content,
156
156
  namespace: namespace,
@@ -314,18 +314,19 @@ class DirAddKbContent {
314
314
  })
315
315
  }
316
316
 
317
- async setDefaultEngine() {
318
- return new Promise((resolve) => {
319
- let engine = {
320
- name: "pinecone",
321
- type: process.env.PINECONE_TYPE,
322
- apikey: "",
323
- vector_size: 1536,
324
- index_name: process.env.PINECONE_INDEX
325
- }
326
- resolve(engine);
327
- })
328
- }
317
+ // async setDefaultEngine() {
318
+ // let isHybrid = hybrid === true;
319
+ // return new Promise((resolve) => {
320
+ // let engine = {
321
+ // name: "pinecone",
322
+ // type: isHybrid ? "serverless" : "pod",
323
+ // apikey: "",
324
+ // vector_size: 1536,
325
+ // index_name: isHybrid ? "hybrid_index" : "standard_index"
326
+ // }
327
+ // resolve(engine);
328
+ // })
329
+ // }
329
330
 
330
331
  }
331
332
 
@@ -11,6 +11,10 @@ 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
+
14
18
 
15
19
  class DirAskGPTV2 {
16
20
 
@@ -165,14 +169,11 @@ class DirAskGPTV2 {
165
169
  }
166
170
  }
167
171
 
168
- const kb_endpoint = process.env.KB_ENDPOINT_QA
169
- winston.verbose("DirAskGPTV2 KbEndpoint URL: " + kb_endpoint);
170
-
171
172
  let key = await integrationService.getKeyFromIntegrations(this.projectId, 'openai', this.token);
172
173
  if (!key) {
173
174
  this.logger.native("[Ask Knowledge Base] OpenAI key not found in Integration. Using shared OpenAI key");
174
175
  winston.verbose("DirAskGPTV2 - Key not found in Integrations. Searching in kb settings...");
175
- key = await this.getKeyFromKbSettings();
176
+ key = await llmService.getKeyFromKbSettings(this.projectId, this.token);
176
177
  }
177
178
 
178
179
  if (!key) {
@@ -196,7 +197,7 @@ class DirAskGPTV2 {
196
197
  }
197
198
 
198
199
  if (publicKey === true && !chunks_only) {
199
- let keep_going = await this.checkQuoteAvailability();
200
+ let keep_going = await quotasService.checkQuoteAvailability(this.projectId, this.token);
200
201
  if (keep_going === false) {
201
202
  this.logger.warn("[Ask Knowledge Base] Tokens quota exceeded. Skip the action")
202
203
  winston.verbose("DirAskGPTV2 - Quota exceeded for tokens. Skip the action")
@@ -207,6 +208,17 @@ class DirAskGPTV2 {
207
208
  }
208
209
  }
209
210
 
211
+ if (!namespace) {
212
+ this.logger.error("[Ask Knowledge Base] Namespace is undefined")
213
+ winston.verbose("DirAskGPTV2 - Error: namespace is undefined")
214
+ if (falseIntent) {
215
+ await this.chatbot.addParameter("flowError", "AskGPT Error: namespace is undefined");
216
+ await this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes);
217
+ callback(true);
218
+ return;
219
+ }
220
+ }
221
+
210
222
  let ns;
211
223
 
212
224
  if (action.namespaceAsName) {
@@ -237,18 +249,7 @@ class DirAskGPTV2 {
237
249
  if (ns.engine) {
238
250
  engine = ns.engine;
239
251
  } else {
240
- engine = await this.setDefaultEngine()
241
- }
242
-
243
- if (!namespace) {
244
- this.logger.error("[Ask Knowledge Base] Namespace is undefined")
245
- winston.verbose("DirAskGPTV2 - Error: namespace is undefined")
246
- if (falseIntent) {
247
- await this.chatbot.addParameter("flowError", "AskGPT Error: namespace is undefined");
248
- await this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes);
249
- callback(true);
250
- return;
251
- }
252
+ engine = await this.setDefaultEngine(ns.hybrid);
252
253
  }
253
254
 
254
255
  let json = {
@@ -273,7 +274,8 @@ class DirAskGPTV2 {
273
274
  json.chunks_only = chunks_only;
274
275
  }
275
276
 
276
- if (engine.type === 'serverless') {
277
+
278
+ if (ns.hybrid === true) {
277
279
  json.search_type = 'hybrid';
278
280
  json.alpha = alpha;
279
281
  }
@@ -294,6 +296,12 @@ class DirAskGPTV2 {
294
296
 
295
297
  winston.debug("DirAskGPTV2 json:", json);
296
298
 
299
+ let kb_endpoint = process.env.KB_ENDPOINT_QA;
300
+ if (ns.hybrid === true) {
301
+ kb_endpoint = process.env.KB_ENDPOINT_QA_GPU;
302
+ }
303
+ winston.verbose("DirAskGPTV2 KbEndpoint URL: " + kb_endpoint);
304
+
297
305
  const HTTPREQUEST = {
298
306
  url: kb_endpoint + "/qa",
299
307
  headers: {
@@ -340,7 +348,9 @@ class DirAskGPTV2 {
340
348
  tokens: resbody.prompt_token_size,
341
349
  model: json.model
342
350
  }
343
- this.updateQuote(tokens_usage);
351
+ quotasService.updateQuote(this.projectId, this.token, tokens_usage).catch((err) => {
352
+ winston.error("Error updating quota: ", err);
353
+ })
344
354
  }
345
355
 
346
356
  if (trueIntent) {
@@ -353,6 +363,10 @@ class DirAskGPTV2 {
353
363
  }
354
364
  } else {
355
365
  await this.#assignAttributes(action, answer, source);
366
+ llmService.addUnansweredQuestion(this.projectId, json.namespace, json.question, this.token).catch((err) => {
367
+ winston.error("DirAskGPTV2 - Error adding unanswered question: ", err);
368
+ this.logger.warn("[Ask Knowledge Base] Unable to add unanswered question", json.question, "to namespacae", json.namespace);
369
+ })
356
370
  if (falseIntent) {
357
371
  await this.#executeCondition(false, trueIntent, trueIntentAttributes, falseIntent, falseIntentAttributes);
358
372
  callback(true);
@@ -423,93 +437,6 @@ class DirAskGPTV2 {
423
437
  }
424
438
  }
425
439
 
426
- async getKeyFromKbSettings() {
427
- return new Promise((resolve) => {
428
-
429
- const KB_HTTPREQUEST = {
430
- url: this.API_ENDPOINT + "/" + this.context.projectId + "/kbsettings",
431
- headers: {
432
- 'Content-Type': 'application/json',
433
- 'Authorization': 'JWT ' + this.context.token
434
- },
435
- method: "GET"
436
- }
437
- winston.debug("DirAskGPTV2 KB HttpRequest", KB_HTTPREQUEST);
438
-
439
- httpUtils.request(
440
- KB_HTTPREQUEST, async (err, resbody) => {
441
- if (err) {
442
- winston.error("DirAskGPTV2 Get kb settings error ", err?.response?.data);
443
- resolve(null);
444
- } else {
445
- if (!resbody.gptkey) {
446
- resolve(null);
447
- } else {
448
- resolve(resbody.gptkey);
449
- }
450
- }
451
- }
452
- )
453
- })
454
- }
455
-
456
- async checkQuoteAvailability() {
457
- return new Promise((resolve) => {
458
-
459
- const HTTPREQUEST = {
460
- url: this.API_ENDPOINT + "/" + this.context.projectId + "/quotes/tokens",
461
- headers: {
462
- 'Content-Type': 'application/json',
463
- 'Authorization': 'JWT ' + this.context.token
464
- },
465
- method: "GET"
466
- }
467
- winston.debug("DirAskGPTV2 check quote availability HttpRequest", HTTPREQUEST);
468
-
469
- httpUtils.request(
470
- HTTPREQUEST, async (err, resbody) => {
471
- if (err) {
472
- winston.error("DirAskGPTV2 Check quote availability err: ", err);
473
- resolve(true)
474
- } else {
475
- if (resbody.isAvailable === true) {
476
- resolve(true)
477
- } else {
478
- resolve(false)
479
- }
480
- }
481
- }
482
- )
483
- })
484
- }
485
-
486
- async updateQuote(tokens_usage) {
487
- return new Promise((resolve, reject) => {
488
-
489
- const HTTPREQUEST = {
490
- url: this.API_ENDPOINT + "/" + this.context.projectId + "/quotes/incr/tokens",
491
- headers: {
492
- 'Content-Type': 'application/json',
493
- 'Authorization': 'JWT ' + this.context.token
494
- },
495
- json: tokens_usage,
496
- method: "POST"
497
- }
498
- winston.debug("DirAskGPTV2 update quote HttpRequest ", HTTPREQUEST);
499
-
500
- httpUtils.request(
501
- HTTPREQUEST, async (err, resbody) => {
502
- if (err) {
503
- winston.error("DirAskGPTV2 Increment tokens quote err: ", err);
504
- reject(false)
505
- } else {
506
- resolve(true);
507
- }
508
- }
509
- )
510
- })
511
- }
512
-
513
440
  /**
514
441
  * Transforms the transcirpt array in a dictionary like '0': { "question": "xxx", "answer":"xxx"}
515
442
  * merging consecutive messages with the same role in a single question or answer.
@@ -590,14 +517,15 @@ class DirAskGPTV2 {
590
517
  })
591
518
  }
592
519
 
593
- async setDefaultEngine() {
520
+ async setDefaultEngine(hybrid = false) {
521
+ let isHybrid = hybrid === true;
594
522
  return new Promise((resolve) => {
595
523
  let engine = {
596
524
  name: "pinecone",
597
- type: process.env.PINECONE_TYPE,
525
+ type: isHybrid ? "serverless" : "pod",
598
526
  apikey: "",
599
527
  vector_size: 1536,
600
- index_name: process.env.PINECONE_INDEX
528
+ index_name: isHybrid ? "hybrid_index" : "standard_index"
601
529
  }
602
530
  resolve(engine);
603
531
  })
@@ -75,6 +75,7 @@ class DirReplyV2 {
75
75
  // }
76
76
 
77
77
  TiledeskChatbotUtil.replaceJSONButtons(message, requestAttributes);
78
+ TiledeskChatbotUtil.replaceJSONGalleries(message, requestAttributes);
78
79
 
79
80
  try {
80
81
  // 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("WebRequest error: ", err);
121
+ this.logger.error("[Web Request] error: ", err);
122
122
  winston.log("webRequest error: ", err);
123
123
  if (callback) {
124
124
  if (falseIntent) {
@@ -90,6 +90,7 @@ class DirWebResponse {
90
90
  }
91
91
  catch (err) {
92
92
  winston.error("Error parsing webRequest jsonBody: " + JSON.stringify(jsonBody) + "\nError: " + JSON.stringify(err));
93
+ this.logger.error("[Web Response] Error parsing webRequest jsonBody ", jsonBody)
93
94
  reject("Error parsing jsonBody");
94
95
  }
95
96
  }
@@ -58,6 +58,7 @@ 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';
61
62
  static WEB_RESPONSE = "web_response";
62
63
  static FLOW_LOG = "flow_log";
63
64
  static ADD_KB_CONTENT = "add_kb_content";