@tiledesk/tiledesk-server 2.19.4 → 2.19.6

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/routes/kb.js CHANGED
@@ -35,12 +35,12 @@ let MAX_UPLOAD_FILE_SIZE = process.env.MAX_UPLOAD_FILE_SIZE;
35
35
  let uploadlimits = undefined;
36
36
 
37
37
  if (MAX_UPLOAD_FILE_SIZE) {
38
- uploadlimits = {fileSize: parseInt(MAX_UPLOAD_FILE_SIZE)} ;
38
+ uploadlimits = { fileSize: parseInt(MAX_UPLOAD_FILE_SIZE) };
39
39
  winston.debug("Max upload file size is : " + MAX_UPLOAD_FILE_SIZE);
40
40
  } else {
41
41
  winston.debug("Max upload file size is infinity");
42
42
  }
43
- var upload = multer({limits: uploadlimits});
43
+ var upload = multer({ limits: uploadlimits });
44
44
 
45
45
  let jobManager = new JobManager(AMQP_MANAGER_URL, {
46
46
  debug: false,
@@ -84,22 +84,23 @@ const default_engine_hybrid = require('../config/kb/engine.hybrid');
84
84
  const default_embedding = require('../config/kb/embedding');
85
85
  const PromptManager = require('../config/kb/prompt/rag/PromptManager');
86
86
  const situatedContext = require('../config/kb/situatedContext');
87
+ const kbQuestionService = require('../services/kbQuestionService');
87
88
 
88
89
  const ragPromptManager = new PromptManager(path.join(__dirname, '../config/kb/prompt/rag'));
89
90
 
90
91
  const RAG_CONTEXT_ENV_OVERRIDES = {
91
- "gpt-3.5-turbo": process.env.GPT_3_5_CONTEXT,
92
- "gpt-4": process.env.GPT_4_CONTEXT,
92
+ "gpt-3.5-turbo": process.env.GPT_3_5_CONTEXT,
93
+ "gpt-4": process.env.GPT_4_CONTEXT,
93
94
  "gpt-4-turbo-preview": process.env.GPT_4T_CONTEXT,
94
- "gpt-4o": process.env.GPT_4O_CONTEXT,
95
- "gpt-4o-mini": process.env.GPT_4O_MINI_CONTEXT,
96
- "gpt-4.1": process.env.GPT_4_1_CONTEXT,
97
- "gpt-4.1-mini": process.env.GPT_4_1_MINI_CONTEXT,
98
- "gpt-4.1-nano": process.env.GPT_4_1_NANO_CONTEXT,
99
- "gpt-5": process.env.GPT_5_CONTEXT,
100
- "gpt-5-mini": process.env.GPT_5_MINI_CONTEXT,
101
- "gpt-5-nano": process.env.GPT_5_NANO_CONTEXT,
102
- "general": process.env.GENERAL_CONTEXT
95
+ "gpt-4o": process.env.GPT_4O_CONTEXT,
96
+ "gpt-4o-mini": process.env.GPT_4O_MINI_CONTEXT,
97
+ "gpt-4.1": process.env.GPT_4_1_CONTEXT,
98
+ "gpt-4.1-mini": process.env.GPT_4_1_MINI_CONTEXT,
99
+ "gpt-4.1-nano": process.env.GPT_4_1_NANO_CONTEXT,
100
+ "gpt-5": process.env.GPT_5_CONTEXT,
101
+ "gpt-5-mini": process.env.GPT_5_MINI_CONTEXT,
102
+ "gpt-5-nano": process.env.GPT_5_NANO_CONTEXT,
103
+ "general": process.env.GENERAL_CONTEXT
103
104
  };
104
105
 
105
106
  /** RAG system prompt per modello: file in config/kb/prompt/rag, sovrascrivibili via env (come prima). */
@@ -114,13 +115,6 @@ function getRagContextTemplate(modelName) {
114
115
  return ragPromptManager.getPrompt(modelName);
115
116
  }
116
117
 
117
- function normalizeEmbedding(embedding) {
118
- const normalizedEmbedding = (embedding && typeof embedding.toObject === 'function')
119
- ? embedding.toObject()
120
- : (embedding || default_embedding);
121
- return { ...normalizedEmbedding };
122
- }
123
-
124
118
  function normalizeSituatedContext(enable = false) {
125
119
  situatedContext.enable = enable;
126
120
  return situatedContext.enable
@@ -142,15 +136,15 @@ router.post('/scrape/single', async (req, res) => {
142
136
 
143
137
  let data = req.body;
144
138
  winston.debug("/scrape/single data: ", data)
145
-
139
+
146
140
  let namespace;
147
141
  try {
148
142
  namespace = await aiManager.checkNamespace(project_id, data.namespace);
149
143
  } catch (err) {
150
144
  let errorCode = err?.errorCode ?? 500;
151
145
  return res.status(errorCode).send({ success: false, error: err.error });
152
- }
153
-
146
+ }
147
+
154
148
  if (data.type === "sitemap") {
155
149
 
156
150
  const urls = await aiManager.fetchSitemap(data.source).catch((err) => {
@@ -166,18 +160,18 @@ router.post('/scrape/single', async (req, res) => {
166
160
  try {
167
161
  sitemapKb = await KB.findById(data.id);
168
162
  } catch (err) {
169
- winston.error("Error finding sitemap content with id " + data.id);
163
+ winston.error("Error finding sitemap content with id " + data.id);
170
164
  return res.status(500).send({ success: false, error: "Error finding sitemap content with id " + data.id });
171
165
  }
172
166
 
173
167
  if (!sitemapKb) {
174
168
  return res.status(404).send({ success: false, error: "Content not found with id " + data.id });
175
169
  }
176
-
170
+
177
171
  let existingKbs;
178
172
  try {
179
173
  existingKbs = await KB.find({ id_project: project_id, namespace: data.namespace, sitemap_origin_id: data.id }).lean().exec();
180
- } catch(err) {
174
+ } catch (err) {
181
175
  winston.error("Error finding existing contents: ", err);
182
176
  return res.status(500).send({ success: false, error: "Error finding existing sitemap contents" });
183
177
  }
@@ -263,8 +257,7 @@ router.post('/scrape/single', async (req, res) => {
263
257
  }
264
258
 
265
259
  json.engine = namespace.engine || default_engine;
266
- json.embedding = normalizeEmbedding(namespace.embedding);
267
- json.embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
260
+ json.embedding = aiManager.normalizeEmbedding(namespace.embedding);
268
261
 
269
262
  if (namespace.hybrid === true) {
270
263
  json.hybrid = true;
@@ -396,7 +389,7 @@ router.post('/qa', async (req, res) => {
396
389
  if (publicKey === true) {
397
390
  let isAvailable = await quoteManager.checkQuote(req.project, obj, 'tokens');
398
391
  if (isAvailable === false) {
399
- return res.status(403).send({ success: false, message: "Tokens quota exceeded", error_code: 13001})
392
+ return res.status(403).send({ success: false, message: "Tokens quota exceeded", error_code: 13001 })
400
393
  }
401
394
  }
402
395
 
@@ -411,15 +404,14 @@ router.post('/qa', async (req, res) => {
411
404
  }
412
405
 
413
406
  data.engine = namespace.engine || default_engine;
414
- data.embedding = normalizeEmbedding(namespace.embedding);
415
- data.embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
407
+ data.embedding = aiManager.normalizeEmbedding(namespace.embedding);
416
408
 
417
409
  if (namespace.hybrid === true) {
418
410
  data.search_type = 'hybrid';
419
-
411
+
420
412
  if (data.reranking === true) {
421
413
  data.reranker_model = process.env.RERANKING_MODEL || "cross-encoder/ms-marco-MiniLM-L-6-v2";
422
-
414
+
423
415
  if (!data.reranking_multiplier) {
424
416
  data.reranking_multiplier = 3;
425
417
  }
@@ -432,10 +424,10 @@ router.post('/qa', async (req, res) => {
432
424
  data.reranking_multiplier = calculatedRerankingMultiplier;
433
425
  }
434
426
  }
435
- }
427
+ }
436
428
 
437
429
  if (!namespace.hybrid && data.reranking === true && PINECONE_RERANKING) {
438
-
430
+
439
431
  data.reranking = {
440
432
  "provider": "pinecone",
441
433
  "api_key": process.env.PINECONE_API_KEY,
@@ -476,7 +468,7 @@ router.post('/qa', async (req, res) => {
476
468
  const sendError = (message) => {
477
469
  try {
478
470
  res.write('data: ' + JSON.stringify({ error: message }) + '\n\n');
479
- } catch (_) {}
471
+ } catch (_) { }
480
472
  res.end();
481
473
  };
482
474
 
@@ -518,6 +510,32 @@ router.post('/qa', async (req, res) => {
518
510
  return false;
519
511
  }
520
512
 
513
+ let streamQaResult = null;
514
+ const qaStartedAt = Date.now();
515
+ let streamDurationSeconds = null;
516
+
517
+ function captureStreamDurationFromFirstChunk(obj) {
518
+ if (streamDurationSeconds != null) return;
519
+ const content = extractContent(obj);
520
+ if (content != null && content !== '') {
521
+ streamDurationSeconds = (Date.now() - qaStartedAt) / 1000;
522
+ }
523
+ }
524
+
525
+ function withMeasuredDuration(payload) {
526
+ if (streamDurationSeconds != null && payload && typeof payload === 'object') {
527
+ payload.duration = streamDurationSeconds;
528
+ }
529
+ return payload;
530
+ }
531
+
532
+ function captureStreamQaResult(obj) {
533
+ const normalized = normalizeKbQaPayload(obj);
534
+ if (normalized && typeof normalized === 'object' && typeof normalized.success === 'boolean') {
535
+ streamQaResult = normalized;
536
+ }
537
+ }
538
+
521
539
  function forwardSsePayload(payload) {
522
540
  if (payload === '[DONE]') return;
523
541
  let obj;
@@ -527,16 +545,19 @@ router.post('/qa', async (req, res) => {
527
545
  return;
528
546
  }
529
547
 
548
+ captureStreamQaResult(obj);
549
+ captureStreamDurationFromFirstChunk(obj);
550
+
530
551
  if (obj.status === 'started') {
531
552
  return;
532
553
  }
533
554
  if (isKbStreamCompletedSummary(obj)) {
534
- res.write('data: ' + JSON.stringify(normalizeKbQaPayload(obj)) + '\n\n');
555
+ res.write('data: ' + JSON.stringify(withMeasuredDuration(normalizeKbQaPayload(obj))) + '\n\n');
535
556
  return;
536
557
  }
537
558
 
538
559
  if (obj.type === 'metadata' || obj.event === 'metadata') {
539
- res.write('data: ' + JSON.stringify(normalizeKbQaPayload(obj)) + '\n\n');
560
+ res.write('data: ' + JSON.stringify(withMeasuredDuration(normalizeKbQaPayload(obj))) + '\n\n');
540
561
  return;
541
562
  }
542
563
  const content = extractContent(obj);
@@ -546,7 +567,7 @@ router.post('/qa', async (req, res) => {
546
567
  }
547
568
  const normalized = normalizeKbQaPayload(obj);
548
569
  if (isMetadataPayload(normalized, content)) {
549
- res.write('data: ' + JSON.stringify(normalized) + '\n\n');
570
+ res.write('data: ' + JSON.stringify(withMeasuredDuration(normalized)) + '\n\n');
550
571
  }
551
572
  }
552
573
 
@@ -567,7 +588,7 @@ router.post('/qa', async (req, res) => {
567
588
  }
568
589
  });
569
590
 
570
- stream.on('end', () => {
591
+ stream.on('end', async () => {
571
592
  const tail = buffer.trim();
572
593
  if (tail) {
573
594
  for (const line of tail.split('\n')) {
@@ -576,6 +597,26 @@ router.post('/qa', async (req, res) => {
576
597
  forwardSsePayload(trimmed.slice(6));
577
598
  }
578
599
  }
600
+ if (streamQaResult) {
601
+ withMeasuredDuration(streamQaResult);
602
+ const tokens = await aiManager.getTrackingTokens({
603
+ qaResult: streamQaResult,
604
+ publicKey,
605
+ model: data.model,
606
+ obj,
607
+ quoteManager,
608
+ project: req.project,
609
+ });
610
+ kbQuestionService.trackKbQaResult(
611
+ id_project,
612
+ { namespace: data.namespace, question: data.question },
613
+ streamQaResult,
614
+ tokens
615
+ );
616
+ if (publicKey === true && typeof streamQaResult.legacy_prompt_token_size === 'number') {
617
+ res.write('data: ' + JSON.stringify(streamQaResult) + '\n\n');
618
+ }
619
+ }
579
620
  res.write('data: [DONE]\n\n');
580
621
  res.end();
581
622
  });
@@ -604,35 +645,29 @@ router.post('/qa', async (req, res) => {
604
645
  return;
605
646
  }
606
647
 
607
- let duration = Date.now();
648
+ const qaStartedAt = Date.now();
608
649
 
609
- aiService.askNamespace(data).then((resp) => {
650
+ aiService.askNamespace(data).then(async (resp) => {
610
651
  winston.debug("qa resp: ", resp.data);
611
652
  let answer = resp.data;
612
653
 
613
- answer.duration = (Date.now() - duration) / 1000;
614
-
615
- if (publicKey === true) {
616
- let modelKey;
617
- if (typeof data.model === 'string') {
618
- modelKey = data.model;
619
- } else if (data.model && typeof data.model.name === 'string') {
620
- modelKey = data.model.name;
621
- }
622
-
623
- let multiplier = MODELS_MULTIPLIER[modelKey];
624
- if (!multiplier) {
625
- multiplier = 1;
626
- winston.info("No multiplier found for AI model (qa) " + modelKey);
627
- }
654
+ answer.duration = (Date.now() - qaStartedAt) / 1000;
628
655
 
629
- obj.multiplier = multiplier;
630
- obj.tokens = answer.prompt_token_size;
656
+ const tokens = await aiManager.getTrackingTokens({
657
+ qaResult: answer,
658
+ publicKey,
659
+ model: data.model,
660
+ obj,
661
+ quoteManager,
662
+ project: req.project,
663
+ });
664
+ kbQuestionService.trackKbQaResult(
665
+ id_project,
666
+ { namespace: data.namespace, question: data.question },
667
+ answer,
668
+ tokens
669
+ );
631
670
 
632
- let incremented_key = quoteManager.incrementTokenCount(req.project, obj);
633
- winston.verbose("incremented_key: ", incremented_key);
634
- }
635
-
636
671
  return res.status(200).send(answer);
637
672
 
638
673
  }).catch((err) => {
@@ -738,7 +773,7 @@ router.post('/qa', async (req, res) => {
738
773
 
739
774
  // if (namespace.hybrid === true) {
740
775
  // data.search_type = 'hybrid';
741
-
776
+
742
777
  // if (data.reranking === true) {
743
778
  // data.reranking_multiplier = 3;
744
779
  // data.reranker_model = "cross-encoder/ms-marco-MiniLM-L-6-v2";
@@ -771,7 +806,7 @@ router.post('/qa', async (req, res) => {
771
806
 
772
807
  // delete data.advancedPrompt;
773
808
  // winston.verbose("ask data: ", data);
774
-
809
+
775
810
  // if (process.env.NODE_ENV === 'test') {
776
811
  // return res.status(200).send({ success: true, message: "Question skipped in test environment", data: data });
777
812
  // }
@@ -794,7 +829,7 @@ router.post('/qa', async (req, res) => {
794
829
  // let incremented_key = quoteManager.incrementTokenCount(req.project, obj);
795
830
  // winston.verbose("incremented_key: ", incremented_key);
796
831
  // }
797
-
832
+
798
833
  // return res.status(200).send(answer);
799
834
 
800
835
  // }).catch((err) => {
@@ -886,7 +921,7 @@ router.get('/namespace/all', async (req, res) => {
886
921
 
887
922
  let project_id = req.projectid;
888
923
 
889
- Namespace.find({ id_project: project_id }).lean().exec( async (err, namespaces) => {
924
+ Namespace.find({ id_project: project_id }).lean().exec(async (err, namespaces) => {
890
925
 
891
926
  if (err) {
892
927
  winston.error("find namespaces error: ", err);
@@ -935,7 +970,7 @@ router.get('/namespace/all', async (req, res) => {
935
970
  } else {
936
971
  namespaceObjArray = namespaces.map(({ _id, __v, ...keepAttrs }) => keepAttrs)
937
972
  }
938
-
973
+
939
974
  winston.debug("namespaceObjArray: ", namespaceObjArray);
940
975
  return res.status(200).send(namespaceObjArray);
941
976
  }
@@ -961,14 +996,14 @@ router.get('/namespace/:id/chunks/:content_id', async (req, res) => {
961
996
  return res.status(403).send({ success: false, error: err })
962
997
  })
963
998
 
964
- if(!content) {
999
+ if (!content) {
965
1000
  return res.status(403).send({ success: false, error: "Not allowed. The content does not belong to the current namespace." })
966
1001
  }
967
1002
 
968
1003
  let engine = namespace.engine || default_engine;
969
1004
 
970
1005
  if (process.env.NODE_ENV === 'test') {
971
- return res.status(200).send({ success: true, message: "Get chunks skipped in test environment"});
1006
+ return res.status(200).send({ success: true, message: "Get chunks skipped in test environment" });
972
1007
  }
973
1008
 
974
1009
  aiService.getContentChunks(namespace_id, content_id, engine).then((resp) => {
@@ -989,7 +1024,7 @@ router.get('/namespace/:id/chatbots', async (req, res) => {
989
1024
 
990
1025
  let project_id = req.projectid;
991
1026
  let namespace_id = req.params.id;
992
-
1027
+
993
1028
  let chatbotsArray = [];
994
1029
 
995
1030
  let namespace;
@@ -1031,12 +1066,12 @@ router.get('/namespace/:id/chatbots', async (req, res) => {
1031
1066
  await Promise.all(chatbotPromises);
1032
1067
 
1033
1068
  winston.debug("chatbotsArray: ", chatbotsArray);
1034
-
1069
+
1035
1070
  res.status(200).send(chatbotsArray);
1036
1071
  })
1037
1072
 
1038
1073
  router.get('/namespace/export/:id', async (req, res) => {
1039
-
1074
+
1040
1075
  let id_project = req.projectid;
1041
1076
  let namespace_id = req.params.id;
1042
1077
 
@@ -1048,7 +1083,7 @@ router.get('/namespace/export/:id', async (req, res) => {
1048
1083
  query.status = parseInt(req.query.status)
1049
1084
  }
1050
1085
 
1051
- query.type = { $in: [ kbTypes.URL, kbTypes.TEXT, kbTypes.FAQ ] };
1086
+ query.type = { $in: [kbTypes.URL, kbTypes.TEXT, kbTypes.FAQ] };
1052
1087
 
1053
1088
  let namespace;
1054
1089
  try {
@@ -1078,11 +1113,11 @@ router.get('/namespace/export/:id', async (req, res) => {
1078
1113
  let json_string = JSON.stringify(json);
1079
1114
  res.set({ "Content-Disposition": `attachment; filename="${filename}.json"` });
1080
1115
  return res.send(json_string);
1081
- } catch(err) {
1116
+ } catch (err) {
1082
1117
  winston.error("Error genereting json ", err);
1083
1118
  return res.status(500).send({ success: false, error: "Error genereting json file" })
1084
1119
  }
1085
-
1120
+
1086
1121
  })
1087
1122
 
1088
1123
  router.post('/namespace', async (req, res) => {
@@ -1212,7 +1247,7 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
1212
1247
  }
1213
1248
 
1214
1249
  let addingContents = [];
1215
- contents.forEach( async (e) => {
1250
+ contents.forEach(async (e) => {
1216
1251
  let content = {
1217
1252
  id_project: id_project,
1218
1253
  name: e.name,
@@ -1254,8 +1289,7 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
1254
1289
  // import operation the content's limit is respected
1255
1290
  let ns = namespaces.find(n => n.id === namespace_id);
1256
1291
  let engine = ns.engine || default_engine;
1257
- let embedding = normalizeEmbedding(ns.embedding);
1258
- embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
1292
+ let embedding = aiManager.normalizeEmbedding(ns.embedding);
1259
1293
  let hybrid = ns.hybrid;
1260
1294
  const situated_context = normalizeSituatedContext();
1261
1295
 
@@ -1295,26 +1329,31 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
1295
1329
 
1296
1330
  let resources = new_contents.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
1297
1331
  resources = resources.map(({ _id, scrape_options, ...rest }) => {
1298
- return {
1299
- id: _id,
1300
- parameters_scrape_type_4: scrape_options,
1301
- embedding: embedding,
1302
- engine: engine,
1303
- ...(situated_context && { situated_context: situated_context }),
1304
- ...rest}
1332
+ return {
1333
+ id: _id,
1334
+ parameters_scrape_type_4: scrape_options,
1335
+ embedding: embedding,
1336
+ engine: engine,
1337
+ ...(situated_context && { situated_context: situated_context }),
1338
+ ...rest
1339
+ }
1305
1340
  });
1306
-
1341
+
1307
1342
  winston.verbose("resources to be sent to worker: ", resources);
1308
1343
 
1309
1344
  if (process.env.NODE_ENV !== "test") {
1310
1345
  aiManager.scheduleScrape(resources, hybrid);
1311
1346
  }
1312
1347
 
1348
+ if (process.env.NODE_ENV === "test") {
1349
+ return res.status(200).send({ success: true, message: "Contents imported successfully", schedule_json: resources });
1350
+ }
1351
+
1313
1352
  res.status(200).send({ success: true, message: "Contents imported successfully" });
1314
1353
 
1315
1354
 
1316
1355
  // saveBulk(operations, addingContents, id_project).then((result) => {
1317
-
1356
+
1318
1357
  // let ns = namespaces.find(n => n.id === namespace_id);
1319
1358
  // let engine = ns.engine || default_engine;
1320
1359
 
@@ -1328,7 +1367,7 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
1328
1367
  // if (process.env.NODE_ENV !== 'test') {
1329
1368
  // scheduleScrape(resources);
1330
1369
  // }
1331
-
1370
+
1332
1371
  // //res.status(200).send(result);
1333
1372
  // res.status(200).send({ success: true, message: "Contents imported successfully"});
1334
1373
 
@@ -1336,7 +1375,7 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
1336
1375
  // winston.error("Unable to save kbs in bulk ", err)
1337
1376
  // res.status(500).send(err);
1338
1377
  // })
1339
-
1378
+
1340
1379
  })
1341
1380
 
1342
1381
  router.put('/namespace/:id', async (req, res) => {
@@ -1614,7 +1653,7 @@ router.post('/', async (req, res) => {
1614
1653
 
1615
1654
  const { name, type, source, content, refresh_rate, scrape_type, scrape_options, tags, situated_context, chunk_regex } = req.body;
1616
1655
  const namespace_id = req.body?.namespace;
1617
-
1656
+
1618
1657
  if (!namespace_id) {
1619
1658
  return res.status(400).send({ success: false, error: "parameter 'namespace' is not defined" });
1620
1659
  }
@@ -1626,11 +1665,11 @@ router.post('/', async (req, res) => {
1626
1665
  let errorCode = err?.errorCode ?? 500;
1627
1666
  return res.status(errorCode).send({ success: false, error: err.error });
1628
1667
  }
1629
-
1668
+
1630
1669
  const quoteManager = req.app.get('quote_manager');
1631
1670
  try {
1632
1671
  await aiManager.checkQuotaAvailability(quoteManager, project, 1)
1633
- } catch(err) {
1672
+ } catch (err) {
1634
1673
  let errorCode = err?.errorCode ?? 500;
1635
1674
  return res.status(errorCode).send({ success: false, error: err.error, plan_limit: err.plan_limit })
1636
1675
  }
@@ -1683,11 +1722,10 @@ router.post('/', async (req, res) => {
1683
1722
  delete raw_content.ok;
1684
1723
  delete raw_content.$clusterTime;
1685
1724
  delete raw_content.operationTime;
1686
-
1725
+
1687
1726
  const saved_kb = raw_content.value;
1688
1727
  const webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
1689
- const embedding = normalizeEmbedding(namespace.embedding);
1690
- embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
1728
+ const embedding = aiManager.normalizeEmbedding(namespace.embedding);
1691
1729
 
1692
1730
  const situated_context_obj = normalizeSituatedContext(saved_kb.situated_context);
1693
1731
 
@@ -1713,7 +1751,7 @@ router.post('/', async (req, res) => {
1713
1751
  if (process.env.NODE_ENV === 'test') {
1714
1752
  return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: raw_content, schedule_json: json });
1715
1753
  }
1716
-
1754
+
1717
1755
  aiManager.scheduleScrape([json], namespace.hybrid);
1718
1756
  return res.status(200).send(raw_content);
1719
1757
 
@@ -1763,7 +1801,7 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
1763
1801
  const quoteManager = req.app.get('quote_manager');
1764
1802
  try {
1765
1803
  await aiManager.checkQuotaAvailability(quoteManager, project, list.length)
1766
- } catch(err) {
1804
+ } catch (err) {
1767
1805
  let errorCode = err?.errorCode ?? 500;
1768
1806
  return res.status(errorCode).send({ success: false, error: err.error, plan_limit: err.plan_limit })
1769
1807
  }
@@ -1844,7 +1882,7 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
1844
1882
  const quoteManager = req.app.get('quote_manager');
1845
1883
  try {
1846
1884
  await aiManager.checkQuotaAvailability(quoteManager, req.project, kbs.length)
1847
- } catch(err) {
1885
+ } catch (err) {
1848
1886
  let errorCode = err?.errorCode ?? 500;
1849
1887
  return res.status(errorCode).send({ success: false, error: err.error, plan_limit: err.plan_limit })
1850
1888
  }
@@ -1863,8 +1901,7 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
1863
1901
  aiManager.saveBulk(operations, kbs, project_id, namespace_id).then((result) => {
1864
1902
 
1865
1903
  let engine = namespace.engine || default_engine;
1866
- let embedding = normalizeEmbedding(namespace.embedding);
1867
- embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
1904
+ let embedding = aiManager.normalizeEmbedding(namespace.embedding);
1868
1905
  let hybrid = namespace.hybrid;
1869
1906
 
1870
1907
  let situated_context_obj;
@@ -1872,15 +1909,15 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
1872
1909
  situated_context_obj = normalizeSituatedContext(situated_context);
1873
1910
  }
1874
1911
 
1875
- let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, situated_context, ...keepAttrs }) => keepAttrs)
1876
- resources = resources.map(({ _id, ...rest}) => {
1877
- return {
1878
- id: _id,
1879
- webhook: webhook,
1880
- embedding: embedding,
1881
- engine: engine,
1882
- ...(situated_context_obj && { situated_context: situated_context_obj }),
1883
- ...rest
1912
+ let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, situated_context, ...keepAttrs }) => keepAttrs)
1913
+ resources = resources.map(({ _id, ...rest }) => {
1914
+ return {
1915
+ id: _id,
1916
+ webhook: webhook,
1917
+ embedding: embedding,
1918
+ engine: engine,
1919
+ ...(situated_context_obj && { situated_context: situated_context_obj }),
1920
+ ...rest
1884
1921
  };
1885
1922
  })
1886
1923
  winston.verbose("resources to be sent to worker: ", resources);
@@ -1888,7 +1925,7 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
1888
1925
  if (process.env.NODE_ENV === 'test') {
1889
1926
  return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: result, schedule_json: resources });
1890
1927
  }
1891
-
1928
+
1892
1929
  aiManager.scheduleScrape(resources, hybrid);
1893
1930
  return res.status(200).send(result);
1894
1931
 
@@ -1920,7 +1957,7 @@ router.post('/sitemap', async (req, res) => {
1920
1957
  winston.debug("data: ", data);
1921
1958
  res.status(200).send(data);
1922
1959
  }).catch((err) => {
1923
- console.error("err ", err)
1960
+ winston.error("Error fetching sitemap: ", err)
1924
1961
  res.status(500).send({ success: false, error: err });
1925
1962
  })
1926
1963
 
@@ -1937,13 +1974,13 @@ router.post('/sitemap/import', async (req, res) => {
1937
1974
  }
1938
1975
 
1939
1976
  if (type !== "sitemap") {
1940
- return res.status(403).send({success: false, error: "Endpoint available for sitemap type only." });
1977
+ return res.status(403).send({ success: false, error: "Endpoint available for sitemap type only." });
1941
1978
  }
1942
1979
 
1943
1980
  if (!namespace_id) {
1944
1981
  return res.status(400).send({ success: false, error: "queryParam 'namespace' is not defined" })
1945
1982
  }
1946
-
1983
+
1947
1984
  let namespace;
1948
1985
  try {
1949
1986
  namespace = await aiManager.checkNamespace(id_project, namespace_id);
@@ -1974,7 +2011,7 @@ router.post('/sitemap/import', async (req, res) => {
1974
2011
 
1975
2012
  if (data.errors && data.errors.length > 0) {
1976
2013
  winston.error("An error occurred during sitemap fetch: ", data.errors[0])
1977
- return res.status(500).send({ success: false, error: "Unable to fecth sitemap due to an error: " + data.errors[0].message})
2014
+ return res.status(500).send({ success: false, error: "Unable to fecth sitemap due to an error: " + data.errors[0].message })
1978
2015
  }
1979
2016
 
1980
2017
  const urls = Array.isArray(data.sites) ? data.sites : [];
@@ -2018,7 +2055,7 @@ router.post('/sitemap/import', async (req, res) => {
2018
2055
  ...(Array.isArray(tags) && tags.length > 0 ? { tags } : {}),
2019
2056
  ...(situated_context && situated_context === true && scrape_type === 0 ? { situated_context: true } : {}),
2020
2057
  }
2021
-
2058
+
2022
2059
  try {
2023
2060
  await aiManager.scheduleSitemap(namespace, saved_content, options);
2024
2061
  let result = await aiManager.addMultipleUrls(namespace, urls, options);
@@ -2030,8 +2067,8 @@ router.post('/sitemap/import', async (req, res) => {
2030
2067
  return res.status(200).send(result);
2031
2068
  } catch (err) {
2032
2069
  return res.status(500).send({ success: false, error: "Unable to add multiple urls from sitemap due to an error." });
2033
- }
2034
-
2070
+ }
2071
+
2035
2072
  })
2036
2073
 
2037
2074
  router.put('/:kb_id', async (req, res) => {
@@ -2039,7 +2076,7 @@ router.put('/:kb_id', async (req, res) => {
2039
2076
  const id_project = req.projectid;
2040
2077
  const project = req.project;
2041
2078
  const kb_id = req.params.kb_id;
2042
-
2079
+
2043
2080
  const { name, type, source, content, refresh_rate, scrape_type, scrape_options, tags } = req.body;
2044
2081
  const namespace_id = req.body.namespace;
2045
2082
  let situated_context = req.body.situated_context;
@@ -2157,8 +2194,7 @@ router.put('/:kb_id', async (req, res) => {
2157
2194
  return res.status(500).send({ success: false, error: err });
2158
2195
  }
2159
2196
 
2160
- const embedding = normalizeEmbedding(namespace.embedding);
2161
- embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
2197
+ const embedding = aiManager.normalizeEmbedding(namespace.embedding);
2162
2198
  let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
2163
2199
  const situated_context_obj = normalizeSituatedContext(updated_content.situated_context);
2164
2200
 
@@ -2183,7 +2219,7 @@ router.put('/:kb_id', async (req, res) => {
2183
2219
  if (process.env.NODE_ENV === 'test') {
2184
2220
  return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: updated_content, schedule_json: json });
2185
2221
  }
2186
-
2222
+
2187
2223
  aiManager.scheduleScrape([json], namespace.hybrid);
2188
2224
  return res.status(200).send(updated_content);
2189
2225
 
@@ -2195,11 +2231,11 @@ router.delete('/:kb_id', async (req, res) => {
2195
2231
  let kb_id = req.params.kb_id;
2196
2232
  winston.verbose("delete kb_id " + kb_id);
2197
2233
 
2198
- let kb = await KB.findOne({ id_project: project_id, _id: kb_id}).catch((err) => {
2234
+ let kb = await KB.findOne({ id_project: project_id, _id: kb_id }).catch((err) => {
2199
2235
  winston.warn("Unable to find kb. Error: ", err);
2200
2236
  return res.status(500).send({ success: false, error: err })
2201
2237
  })
2202
-
2238
+
2203
2239
  if (!kb) {
2204
2240
  winston.error("Unable to delete kb. Kb not found...")
2205
2241
  return res.status(404).send({ success: false, error: "Content not found" })
@@ -2218,7 +2254,7 @@ router.delete('/:kb_id', async (req, res) => {
2218
2254
  if (kb.type === 'sitemap') {
2219
2255
  try {
2220
2256
  await aiManager.deleteSitemap(kb, namespace);
2221
- console.log("Scheduled jobs for deleting sitemap");
2257
+ winston.info("Scheduled jobs for deleting sitemap");
2222
2258
  //return res.status(200).send({ success: true, message: "Scheduled jobs for deleting sitemap" });
2223
2259
  } catch (err) {
2224
2260
  winston.error("Error deleting sitemap: ", err);
@@ -2234,7 +2270,7 @@ router.delete('/:kb_id', async (req, res) => {
2234
2270
  if (!data.namespace) {
2235
2271
  data.namespace = project_id;
2236
2272
  }
2237
-
2273
+
2238
2274
  data.engine = namespace.engine || default_engine;
2239
2275
  winston.verbose("/:delete_id data: ", data);
2240
2276