@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/CHANGELOG.md +7 -0
- package/models/kb_setting.js +2 -99
- package/package.json +1 -1
- package/routes/answered.js +243 -264
- package/routes/kb.js +162 -126
- package/routes/unanswered.js +277 -296
- package/routes/webhook.js +1 -2
- package/services/aiManager.js +41 -23
- package/services/kbQuestionService.js +98 -0
- package/test/kbRoute.js +148 -7
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":
|
|
92
|
-
"gpt-4":
|
|
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":
|
|
95
|
-
"gpt-4o-mini":
|
|
96
|
-
"gpt-4.1":
|
|
97
|
-
"gpt-4.1-mini":
|
|
98
|
-
"gpt-4.1-nano":
|
|
99
|
-
"gpt-5":
|
|
100
|
-
"gpt-5-mini":
|
|
101
|
-
"gpt-5-nano":
|
|
102
|
-
"general":
|
|
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 " +
|
|
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
|
-
|
|
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() -
|
|
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
|
-
|
|
630
|
-
|
|
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(
|
|
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: [
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|