@tiledesk/tiledesk-server 2.15.7 → 2.16.0
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 +10 -1
- package/event/messageEvent.js +24 -0
- package/models/kb_setting.js +5 -0
- package/models/request.js +1 -0
- package/package.json +2 -2
- package/pubmodules/queue/reconnect.js +16 -0
- package/pubmodules/routing-queue/listenerQueued.js +22 -3
- package/pubmodules/scheduler/tasks/closeAgentUnresponsiveRequestTask.js +2 -1
- package/pubmodules/scheduler/tasks/closeBotUnresponsiveRequestTask.js +2 -1
- package/routes/kb.js +278 -120
- package/routes/request.js +174 -92
- package/services/aiManager.js +72 -3
- package/test/fixtures/example-kb-faqs.csv +2 -2
- package/test/fixtures/exported_namespace.json +2 -1
- package/test/kbRoute.js +125 -12
- package/utils/arrayUtil.js +35 -1
package/routes/kb.js
CHANGED
|
@@ -13,6 +13,7 @@ const faq = require('../models/faq');
|
|
|
13
13
|
const faq_kb = require('../models/faq_kb');
|
|
14
14
|
|
|
15
15
|
const { MODELS_MULTIPLIER } = require('../utils/aiUtils');
|
|
16
|
+
const { parseStringArrayField } = require('../utils/arrayUtil');
|
|
16
17
|
const { kbTypes } = require('../models/kbConstants');
|
|
17
18
|
const Sitemapper = require('sitemapper');
|
|
18
19
|
|
|
@@ -80,6 +81,13 @@ const default_engine = require('../config/kb/engine');
|
|
|
80
81
|
const default_engine_hybrid = require('../config/kb/engine.hybrid');
|
|
81
82
|
const default_embedding = require('../config/kb/embedding');
|
|
82
83
|
|
|
84
|
+
function normalizeEmbedding(embedding) {
|
|
85
|
+
const normalizedEmbedding = (embedding && typeof embedding.toObject === 'function')
|
|
86
|
+
? embedding.toObject()
|
|
87
|
+
: (embedding || default_embedding);
|
|
88
|
+
return { ...normalizedEmbedding };
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
let contexts = {
|
|
84
92
|
"gpt-3.5-turbo": process.env.GPT_3_5_CONTEXT || "You are an helpful assistant for question-answering tasks.\nUse ONLY the pieces of retrieved context delimited by #### and the chat history to answer the question.\nIf you don't know the answer, just say: \"I don't know<NOANS>\"\n\n####{context}####",
|
|
85
93
|
"gpt-4": process.env.GPT_4_CONTEXT || "You are an helpful assistant for question-answering tasks.\nUse ONLY the pieces of retrieved context delimited by #### and the chat history to answer the question.\nIf you don't know the answer, just say that you don't know.\nIf and only if none of the retrieved context is useful for your task, add this word to the end <NOANS>\n\n####{context}####",
|
|
@@ -170,7 +178,8 @@ router.post('/scrape/single', async (req, res) => {
|
|
|
170
178
|
sitemap_origin: sitemapKb.source,
|
|
171
179
|
scrape_type: sitemapKb.scrape_type,
|
|
172
180
|
scrape_options: sitemapKb.scrape_options,
|
|
173
|
-
refresh_rate: sitemapKb.refresh_rate
|
|
181
|
+
refresh_rate: sitemapKb.refresh_rate,
|
|
182
|
+
tags: sitemapKb.tags
|
|
174
183
|
}
|
|
175
184
|
aiManager.addMultipleUrls(namespace, addedUrls, options).catch((err) => {
|
|
176
185
|
winston.error("(webhook) error adding multiple urls contents: ", err);
|
|
@@ -215,8 +224,12 @@ router.post('/scrape/single', async (req, res) => {
|
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
|
|
227
|
+
if (kb.tags) {
|
|
228
|
+
json.tags = kb.tags;
|
|
229
|
+
}
|
|
230
|
+
|
|
218
231
|
json.engine = namespace.engine || default_engine;
|
|
219
|
-
json.embedding = namespace.embedding
|
|
232
|
+
json.embedding = normalizeEmbedding(namespace.embedding);
|
|
220
233
|
json.embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
221
234
|
|
|
222
235
|
if (namespace.hybrid === true) {
|
|
@@ -357,7 +370,7 @@ router.post('/qa', async (req, res) => {
|
|
|
357
370
|
}
|
|
358
371
|
|
|
359
372
|
data.engine = namespace.engine || default_engine;
|
|
360
|
-
data.embedding = namespace.embedding
|
|
373
|
+
data.embedding = normalizeEmbedding(namespace.embedding);
|
|
361
374
|
data.embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
362
375
|
|
|
363
376
|
if (namespace.hybrid === true) {
|
|
@@ -989,7 +1002,8 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
|
|
|
989
1002
|
type: e.type,
|
|
990
1003
|
content: e.content,
|
|
991
1004
|
namespace: namespace_id,
|
|
992
|
-
status: -1
|
|
1005
|
+
status: -1,
|
|
1006
|
+
...(e.tags && { tags: e.tags })
|
|
993
1007
|
}
|
|
994
1008
|
|
|
995
1009
|
const optionalFields = ['scrape_type', 'scrape_options', 'refresh_rate'];
|
|
@@ -1022,7 +1036,7 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
|
|
|
1022
1036
|
// import operation the content's limit is respected
|
|
1023
1037
|
let ns = namespaces.find(n => n.id === namespace_id);
|
|
1024
1038
|
let engine = ns.engine || default_engine;
|
|
1025
|
-
let embedding = ns.embedding
|
|
1039
|
+
let embedding = normalizeEmbedding(ns.embedding);
|
|
1026
1040
|
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
1027
1041
|
let hybrid = ns.hybrid;
|
|
1028
1042
|
|
|
@@ -1044,10 +1058,13 @@ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, re
|
|
|
1044
1058
|
|
|
1045
1059
|
winston.verbose("Content deletetion response: ", deleteResponse);
|
|
1046
1060
|
|
|
1047
|
-
|
|
1061
|
+
try {
|
|
1062
|
+
let addedContents = await KB.insertMany(addingContents);
|
|
1063
|
+
winston.debug("addedContents: ", addedContents);
|
|
1064
|
+
} catch (err) {
|
|
1048
1065
|
winston.error("Error adding contents with insertMany: ", err);
|
|
1049
1066
|
return res.status(500).send({ success: true, error: "Error importing contents" });
|
|
1050
|
-
}
|
|
1067
|
+
}
|
|
1051
1068
|
|
|
1052
1069
|
let new_contents;
|
|
1053
1070
|
try {
|
|
@@ -1362,59 +1379,63 @@ router.get('/:kb_id', async (req, res) => {
|
|
|
1362
1379
|
|
|
1363
1380
|
router.post('/', async (req, res) => {
|
|
1364
1381
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
let namespace_id = body.namespace;
|
|
1382
|
+
const id_project = req.projectid;
|
|
1383
|
+
const project = req.project
|
|
1368
1384
|
|
|
1369
|
-
|
|
1385
|
+
const { name, type, source, content, refresh_rate, scrape_type, scrape_options, tags } = req.body;
|
|
1386
|
+
const namespace_id = req.body?.namespace;
|
|
1387
|
+
|
|
1388
|
+
if (!namespace_id) {
|
|
1370
1389
|
return res.status(400).send({ success: false, error: "parameter 'namespace' is not defined" });
|
|
1371
1390
|
}
|
|
1372
1391
|
|
|
1373
1392
|
let namespace;
|
|
1374
1393
|
try {
|
|
1375
|
-
namespace = await aiManager.checkNamespace(
|
|
1394
|
+
namespace = await aiManager.checkNamespace(id_project, namespace_id);
|
|
1376
1395
|
} catch (err) {
|
|
1377
1396
|
let errorCode = err?.errorCode ?? 500;
|
|
1378
1397
|
return res.status(errorCode).send({ success: false, error: err.error });
|
|
1379
1398
|
}
|
|
1380
1399
|
|
|
1381
|
-
|
|
1400
|
+
const quoteManager = req.app.get('quote_manager');
|
|
1382
1401
|
try {
|
|
1383
|
-
await aiManager.checkQuotaAvailability(quoteManager,
|
|
1402
|
+
await aiManager.checkQuotaAvailability(quoteManager, project, 1)
|
|
1384
1403
|
} catch(err) {
|
|
1385
1404
|
let errorCode = err?.errorCode ?? 500;
|
|
1386
1405
|
return res.status(errorCode).send({ success: false, error: err.error, plan_limit: err.plan_limit })
|
|
1387
1406
|
}
|
|
1388
1407
|
|
|
1389
1408
|
let new_kb = {
|
|
1390
|
-
id_project
|
|
1391
|
-
name
|
|
1392
|
-
type
|
|
1393
|
-
source
|
|
1394
|
-
content
|
|
1395
|
-
namespace:
|
|
1409
|
+
id_project,
|
|
1410
|
+
name,
|
|
1411
|
+
type,
|
|
1412
|
+
source,
|
|
1413
|
+
content,
|
|
1414
|
+
namespace: namespace_id,
|
|
1396
1415
|
status: -1
|
|
1397
1416
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
if (new_kb.type === 'txt') {
|
|
1417
|
+
|
|
1418
|
+
if (type === 'txt') {
|
|
1402
1419
|
new_kb.scrape_type = 1;
|
|
1403
1420
|
}
|
|
1404
|
-
if (
|
|
1405
|
-
new_kb.refresh_rate =
|
|
1406
|
-
if (!
|
|
1421
|
+
if (type === 'url') {
|
|
1422
|
+
new_kb.refresh_rate = refresh_rate || 'never';
|
|
1423
|
+
if (!scrape_type || scrape_type === 2) {
|
|
1407
1424
|
new_kb.scrape_type = 2;
|
|
1408
1425
|
new_kb.scrape_options = aiManager.setDefaultScrapeOptions();
|
|
1409
1426
|
} else {
|
|
1410
|
-
new_kb.scrape_type =
|
|
1411
|
-
new_kb.scrape_options =
|
|
1427
|
+
new_kb.scrape_type = scrape_type;
|
|
1428
|
+
new_kb.scrape_options = scrape_options;
|
|
1412
1429
|
}
|
|
1413
1430
|
}
|
|
1414
1431
|
|
|
1432
|
+
if (tags && Array.isArray(tags) && tags.every(tag => typeof tag === "string")) {
|
|
1433
|
+
new_kb.tags = tags;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1415
1436
|
winston.debug("adding kb: ", new_kb);
|
|
1416
1437
|
|
|
1417
|
-
KB.findOneAndUpdate({ id_project
|
|
1438
|
+
KB.findOneAndUpdate({ id_project, type, source }, new_kb, { upsert: true, new: true, rawResult: true }, async (err, raw_content) => {
|
|
1418
1439
|
if (err) {
|
|
1419
1440
|
winston.error("findOneAndUpdate with upsert error: ", err);
|
|
1420
1441
|
res.status(500).send({ success: false, error: err });
|
|
@@ -1425,44 +1446,33 @@ router.post('/', async (req, res) => {
|
|
|
1425
1446
|
delete raw_content.$clusterTime;
|
|
1426
1447
|
delete raw_content.operationTime;
|
|
1427
1448
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1449
|
+
const saved_kb = raw_content.value;
|
|
1450
|
+
const webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
|
|
1451
|
+
const embedding = normalizeEmbedding(namespace.embedding);
|
|
1452
|
+
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
1430
1453
|
|
|
1431
|
-
|
|
1454
|
+
const json = {
|
|
1432
1455
|
id: saved_kb._id,
|
|
1433
1456
|
type: saved_kb.type,
|
|
1434
1457
|
source: saved_kb.source,
|
|
1435
|
-
content: "",
|
|
1458
|
+
content: saved_kb.content || "",
|
|
1436
1459
|
namespace: saved_kb.namespace,
|
|
1437
|
-
webhook: webhook
|
|
1460
|
+
webhook: webhook,
|
|
1461
|
+
hybrid: namespace.hybrid,
|
|
1462
|
+
engine: namespace.engine || default_engine,
|
|
1463
|
+
embedding: embedding,
|
|
1464
|
+
...(saved_kb.scrape_type && { scrape_type: saved_kb.scrape_type }),
|
|
1465
|
+
...(saved_kb.scrape_options && { parameters_scrape_type_4: saved_kb.scrape_options }),
|
|
1466
|
+
...(saved_kb.tags && { tags: saved_kb.tags }),
|
|
1438
1467
|
}
|
|
1439
|
-
winston.debug("json: ", json);
|
|
1440
1468
|
|
|
1441
|
-
|
|
1442
|
-
json.content = saved_kb.content;
|
|
1443
|
-
}
|
|
1444
|
-
if (saved_kb.scrape_type) {
|
|
1445
|
-
json.scrape_type = saved_kb.scrape_type;
|
|
1446
|
-
}
|
|
1447
|
-
if (saved_kb.scrape_options) {
|
|
1448
|
-
json.parameters_scrape_type_4 = saved_kb.scrape_options;
|
|
1449
|
-
}
|
|
1450
|
-
json.engine = namespace.engine || default_engine;
|
|
1451
|
-
json.hybrid = namespace.hybrid;
|
|
1452
|
-
|
|
1453
|
-
let embedding = namespace.embedding || default_embedding;
|
|
1454
|
-
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
1455
|
-
json.embedding = embedding;
|
|
1456
|
-
|
|
1457
|
-
let resources = [];
|
|
1469
|
+
winston.debug("json: ", json);
|
|
1458
1470
|
|
|
1459
|
-
resources.push(json);
|
|
1460
|
-
|
|
1461
1471
|
if (process.env.NODE_ENV === 'test') {
|
|
1462
1472
|
return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: raw_content, schedule_json: json });
|
|
1463
1473
|
}
|
|
1464
1474
|
|
|
1465
|
-
aiManager.scheduleScrape(
|
|
1475
|
+
aiManager.scheduleScrape([json], namespace.hybrid);
|
|
1466
1476
|
return res.status(200).send(raw_content);
|
|
1467
1477
|
|
|
1468
1478
|
}
|
|
@@ -1480,30 +1490,37 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
|
|
|
1480
1490
|
list = req.body.list;
|
|
1481
1491
|
}
|
|
1482
1492
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
let scrape_type = req.body
|
|
1486
|
-
let
|
|
1487
|
-
|
|
1488
|
-
|
|
1493
|
+
const id_project = req.projectid;
|
|
1494
|
+
const project = req.project;
|
|
1495
|
+
let { refresh_rate = 'never', scrape_type = 2, scrape_options } = req.body;
|
|
1496
|
+
let tags = parseStringArrayField(req.body.tags);
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
if (scrape_type === 2 && !scrape_options) {
|
|
1500
|
+
try {
|
|
1501
|
+
scrape_options = aiManager.setDefaultScrapeOptions();
|
|
1502
|
+
} catch (err) {
|
|
1503
|
+
winston.error("Error setting default scrape options: ", err);
|
|
1504
|
+
return res.status(500).send({ success: false, error: err.error });
|
|
1505
|
+
}
|
|
1489
1506
|
}
|
|
1490
1507
|
|
|
1491
|
-
|
|
1508
|
+
const namespace_id = req.query.namespace;
|
|
1492
1509
|
if (!namespace_id) {
|
|
1493
1510
|
return res.status(400).send({ success: false, error: "queryParam 'namespace' is not defined" })
|
|
1494
1511
|
}
|
|
1495
|
-
|
|
1496
1512
|
let namespace;
|
|
1497
1513
|
try {
|
|
1498
|
-
namespace = await aiManager.checkNamespace(
|
|
1514
|
+
namespace = await aiManager.checkNamespace(id_project, namespace_id);
|
|
1499
1515
|
} catch (err) {
|
|
1516
|
+
winston.error("Error checking namespace: ", err);
|
|
1500
1517
|
let errorCode = err?.errorCode ?? 500;
|
|
1501
1518
|
return res.status(errorCode).send({ success: false, error: err.error });
|
|
1502
1519
|
}
|
|
1503
1520
|
|
|
1504
|
-
|
|
1521
|
+
const quoteManager = req.app.get('quote_manager');
|
|
1505
1522
|
try {
|
|
1506
|
-
await aiManager.checkQuotaAvailability(quoteManager,
|
|
1523
|
+
await aiManager.checkQuotaAvailability(quoteManager, project, list.length)
|
|
1507
1524
|
} catch(err) {
|
|
1508
1525
|
let errorCode = err?.errorCode ?? 500;
|
|
1509
1526
|
return res.status(errorCode).send({ success: false, error: err.error, plan_limit: err.plan_limit })
|
|
@@ -1515,14 +1532,14 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
|
|
|
1515
1532
|
}
|
|
1516
1533
|
|
|
1517
1534
|
const options = {
|
|
1518
|
-
scrape_type
|
|
1519
|
-
scrape_options
|
|
1520
|
-
refresh_rate
|
|
1535
|
+
scrape_type,
|
|
1536
|
+
scrape_options,
|
|
1537
|
+
refresh_rate,
|
|
1538
|
+
...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
|
|
1521
1539
|
}
|
|
1522
1540
|
|
|
1523
|
-
let result;
|
|
1524
1541
|
try {
|
|
1525
|
-
result = await aiManager.addMultipleUrls(namespace, list, options);
|
|
1542
|
+
const result = await aiManager.addMultipleUrls(namespace, list, options);
|
|
1526
1543
|
return res.status(200).send(result);
|
|
1527
1544
|
} catch (err) {
|
|
1528
1545
|
winston.error("addMultipleUrls error: ", err)
|
|
@@ -1539,8 +1556,9 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
|
|
|
1539
1556
|
let csv = req.file.buffer.toString('utf8');
|
|
1540
1557
|
winston.debug("csv: ", csv);
|
|
1541
1558
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1559
|
+
const { delimiter = ';' } = req.body;
|
|
1560
|
+
let tags = parseStringArrayField(req.body.tags);
|
|
1561
|
+
|
|
1544
1562
|
|
|
1545
1563
|
let namespace;
|
|
1546
1564
|
try {
|
|
@@ -1567,14 +1585,15 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
|
|
|
1567
1585
|
type: 'faq',
|
|
1568
1586
|
content: question + "\n" + answer,
|
|
1569
1587
|
namespace: namespace_id,
|
|
1570
|
-
status: -1
|
|
1588
|
+
status: -1,
|
|
1589
|
+
...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
|
|
1571
1590
|
})
|
|
1572
1591
|
})
|
|
1573
1592
|
.on("end", async () => {
|
|
1574
1593
|
winston.debug("kbs after CSV parsing: ", kbs);
|
|
1575
1594
|
|
|
1595
|
+
const quoteManager = req.app.get('quote_manager');
|
|
1576
1596
|
try {
|
|
1577
|
-
let quoteManager = req.app.get('quote_manager');
|
|
1578
1597
|
await aiManager.checkQuotaAvailability(quoteManager, req.project, kbs.length)
|
|
1579
1598
|
} catch(err) {
|
|
1580
1599
|
let errorCode = err?.errorCode ?? 500;
|
|
@@ -1595,7 +1614,7 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
|
|
|
1595
1614
|
aiManager.saveBulk(operations, kbs, project_id, namespace_id).then((result) => {
|
|
1596
1615
|
|
|
1597
1616
|
let engine = namespace.engine || default_engine;
|
|
1598
|
-
let embedding = namespace.embedding
|
|
1617
|
+
let embedding = normalizeEmbedding(namespace.embedding);
|
|
1599
1618
|
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
1600
1619
|
let hybrid = namespace.hybrid;
|
|
1601
1620
|
|
|
@@ -1648,11 +1667,15 @@ router.post('/sitemap', async (req, res) => {
|
|
|
1648
1667
|
|
|
1649
1668
|
router.post('/sitemap/import', async (req, res) => {
|
|
1650
1669
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
let content = req.body;
|
|
1670
|
+
const id_project = req.projectid;
|
|
1671
|
+
const namespace_id = req.query.namespace;
|
|
1654
1672
|
|
|
1655
|
-
|
|
1673
|
+
let { type, source, refresh_rate = 'never', scrape_type = 2, scrape_options, tags } = req.body;
|
|
1674
|
+
if (scrape_type === 2 && !scrape_options) {
|
|
1675
|
+
scrape_options = aiManager.setDefaultScrapeOptions();
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
if (type !== "sitemap") {
|
|
1656
1679
|
return res.status(403).send({success: false, error: "Endpoint available for sitemap type only." });
|
|
1657
1680
|
}
|
|
1658
1681
|
|
|
@@ -1662,13 +1685,12 @@ router.post('/sitemap/import', async (req, res) => {
|
|
|
1662
1685
|
|
|
1663
1686
|
let namespace;
|
|
1664
1687
|
try {
|
|
1665
|
-
namespace = await aiManager.checkNamespace(
|
|
1688
|
+
namespace = await aiManager.checkNamespace(id_project, namespace_id);
|
|
1666
1689
|
} catch (err) {
|
|
1667
1690
|
let errorCode = err?.errorCode ?? 500;
|
|
1668
1691
|
return res.status(errorCode).send({ success: false, error: err.error });
|
|
1669
1692
|
}
|
|
1670
1693
|
|
|
1671
|
-
let sitemap_url = req.body.source;
|
|
1672
1694
|
|
|
1673
1695
|
// let quoteManager = req.app.get('quote_manager');
|
|
1674
1696
|
// let limits = await quoteManager.getPlanLimits(req.project);
|
|
@@ -1679,7 +1701,7 @@ router.post('/sitemap/import', async (req, res) => {
|
|
|
1679
1701
|
// winston.verbose("Kbs count: " + kbs_count);
|
|
1680
1702
|
|
|
1681
1703
|
const sitemap = new Sitemapper({
|
|
1682
|
-
url:
|
|
1704
|
+
url: source,
|
|
1683
1705
|
timeout: 15000,
|
|
1684
1706
|
debug: false
|
|
1685
1707
|
});
|
|
@@ -1704,41 +1726,34 @@ router.post('/sitemap/import', async (req, res) => {
|
|
|
1704
1726
|
// return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
|
|
1705
1727
|
// }
|
|
1706
1728
|
|
|
1707
|
-
let refresh_rate = req.body.refresh_rate;
|
|
1708
|
-
let scrape_type = req.body.scrape_type ?? 2;
|
|
1709
|
-
let scrape_options = req.body.scrape_options;
|
|
1710
|
-
if (scrape_type === 2 && scrape_options == null) {
|
|
1711
|
-
scrape_options = aiManager.setDefaultScrapeOptions();
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
1729
|
let sitemap_content = {
|
|
1715
|
-
id_project
|
|
1716
|
-
name:
|
|
1717
|
-
source:
|
|
1730
|
+
id_project,
|
|
1731
|
+
name: source,
|
|
1732
|
+
source: source,
|
|
1718
1733
|
type: 'sitemap',
|
|
1719
1734
|
content: "",
|
|
1720
1735
|
namespace: namespace_id,
|
|
1721
|
-
scrape_type
|
|
1722
|
-
scrape_options
|
|
1723
|
-
refresh_rate
|
|
1736
|
+
scrape_type,
|
|
1737
|
+
scrape_options,
|
|
1738
|
+
refresh_rate,
|
|
1739
|
+
...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
|
|
1724
1740
|
}
|
|
1725
1741
|
|
|
1726
1742
|
let saved_content;
|
|
1727
1743
|
try {
|
|
1728
|
-
saved_content = await KB.findOneAndUpdate({ id_project
|
|
1744
|
+
saved_content = await KB.findOneAndUpdate({ id_project, type: 'sitemap', source, namespace: namespace_id }, sitemap_content, { upsert: true, new: true }).lean().exec();
|
|
1729
1745
|
} catch (err) {
|
|
1730
1746
|
winston.error("Error saving content: ", err);
|
|
1731
1747
|
return res.status(500).send({ success: false, error: err });
|
|
1732
1748
|
}
|
|
1733
1749
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
1750
|
const options = {
|
|
1737
1751
|
sitemap_origin_id: saved_content._id,
|
|
1738
1752
|
sitemap_origin: saved_content.source,
|
|
1739
|
-
scrape_type
|
|
1740
|
-
scrape_options
|
|
1741
|
-
refresh_rate
|
|
1753
|
+
scrape_type,
|
|
1754
|
+
scrape_options,
|
|
1755
|
+
refresh_rate,
|
|
1756
|
+
...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
|
|
1742
1757
|
}
|
|
1743
1758
|
|
|
1744
1759
|
try {
|
|
@@ -1758,38 +1773,181 @@ router.post('/sitemap/import', async (req, res) => {
|
|
|
1758
1773
|
|
|
1759
1774
|
router.put('/:kb_id', async (req, res) => {
|
|
1760
1775
|
|
|
1761
|
-
|
|
1762
|
-
|
|
1776
|
+
const id_project = req.projectid;
|
|
1777
|
+
const project = req.project;
|
|
1778
|
+
const kb_id = req.params.kb_id;
|
|
1779
|
+
|
|
1780
|
+
const { name, type, source, content, refresh_rate, scrape_type, scrape_options, tags } = req.body;
|
|
1781
|
+
const namespace_id = req.body.namespace;
|
|
1763
1782
|
|
|
1764
|
-
|
|
1783
|
+
if (!namespace_id) {
|
|
1784
|
+
return res.status(400).send({ success: false, error: "Missing 'namespace' body parameter" })
|
|
1785
|
+
}
|
|
1765
1786
|
|
|
1766
|
-
|
|
1767
|
-
|
|
1787
|
+
let namespace;
|
|
1788
|
+
try {
|
|
1789
|
+
namespace = await aiManager.checkNamespace(id_project, namespace_id);
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
let errorCode = err?.errorCode ?? 500;
|
|
1792
|
+
return res.status(errorCode).send({ success: false, error: err.error });
|
|
1768
1793
|
}
|
|
1769
1794
|
|
|
1770
|
-
|
|
1771
|
-
|
|
1795
|
+
let kb = await KB.findOne({ id_project, namespace: namespace_id, _id: kb_id }).lean().exec();
|
|
1796
|
+
|
|
1797
|
+
if (!kb) {
|
|
1798
|
+
return res.status(404).send({ success: false, error: "Content not found. Unable to update a non-existing content" })
|
|
1772
1799
|
}
|
|
1773
1800
|
|
|
1774
|
-
|
|
1801
|
+
let data = {
|
|
1802
|
+
id: kb._id,
|
|
1803
|
+
namespace: namespace_id,
|
|
1804
|
+
engine: namespace.engine || default_engine
|
|
1805
|
+
}
|
|
1775
1806
|
|
|
1776
|
-
|
|
1807
|
+
// if (kb.type === 'sitemap') {
|
|
1808
|
+
// let new_sitemap = {
|
|
1809
|
+
// id_project,
|
|
1810
|
+
// name,
|
|
1811
|
+
// source,
|
|
1812
|
+
// type: 'sitemap',
|
|
1813
|
+
// content: "",
|
|
1814
|
+
// namespace: namespace_id,
|
|
1815
|
+
// status: -1,
|
|
1816
|
+
// scrape_type,
|
|
1817
|
+
// scrape_options,
|
|
1818
|
+
// refresh_rate,
|
|
1819
|
+
// ...(Array.isArray(tags) && tags.length > 0 ? { tags } : {})
|
|
1820
|
+
// }
|
|
1777
1821
|
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1822
|
+
// try {
|
|
1823
|
+
// let result = await aiManager.updateSitemap(id_project, namespace, kb, new_sitemap);
|
|
1824
|
+
// return res.status(200).send(result);
|
|
1825
|
+
// } catch (err) {
|
|
1826
|
+
// winston.error("Error updating sitemap: ", err);
|
|
1827
|
+
// return res.status(500).send({ success: false, error: err });
|
|
1828
|
+
// }
|
|
1829
|
+
// }
|
|
1830
|
+
|
|
1831
|
+
try {
|
|
1832
|
+
let delete_response = await aiService.deleteIndex(data);
|
|
1833
|
+
|
|
1834
|
+
if (delete_response.data.success === true) {
|
|
1835
|
+
await KB.findOneAndDelete({ id_project, namespace: namespace_id, source }).lean().exec();
|
|
1836
|
+
// continue the flow
|
|
1837
|
+
} else {
|
|
1838
|
+
winston.error("Unable to update content due to an error: ", delete_response.data.error);
|
|
1839
|
+
return res.status(500).send({ success: false, error: delete_response.data.error });
|
|
1781
1840
|
}
|
|
1782
1841
|
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1842
|
+
} catch (err) {
|
|
1843
|
+
winston.error("Error updating content: ", err);
|
|
1844
|
+
return res.status(500).send({ success: false, error: err });
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
let new_content = {
|
|
1848
|
+
id_project,
|
|
1849
|
+
name,
|
|
1850
|
+
type,
|
|
1851
|
+
source,
|
|
1852
|
+
content,
|
|
1853
|
+
namespace: namespace_id,
|
|
1854
|
+
status: -1,
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
if (new_content.type === 'url') {
|
|
1858
|
+
new_content.refresh_rate = refresh_rate || 'never';
|
|
1859
|
+
if (!scrape_type || scrape_type === 2) {
|
|
1860
|
+
new_content.scrape_type = 2;
|
|
1861
|
+
new_content.scrape_options = aiManager.setDefaultScrapeOptions();
|
|
1862
|
+
} else {
|
|
1863
|
+
new_content.scrape_type = scrape_type;
|
|
1864
|
+
new_content.scrape_options = scrape_options;
|
|
1786
1865
|
}
|
|
1866
|
+
}
|
|
1787
1867
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1868
|
+
if (kb.sitemap_origin_id) {
|
|
1869
|
+
new_content.sitemap_origin_id = kb.sitemap_origin_id;
|
|
1870
|
+
new_content.sitemap_origin = kb.sitemap_origin;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
if (tags && Array.isArray(tags) && tags.every(tag => typeof tag === "string")) {
|
|
1874
|
+
new_content.tags = tags;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
winston.debug("Update content. New content: ", new_content);
|
|
1878
|
+
|
|
1879
|
+
let updated_content;
|
|
1880
|
+
try {
|
|
1881
|
+
updated_content = await KB.findOneAndUpdate({ id_project, namespace: namespace_id, source }, new_content, { upsert: true, new: true }).lean().exec();
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
winston.error("Error updating content: ", err);
|
|
1884
|
+
return res.status(500).send({ success: false, error: err });
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
const embedding = normalizeEmbedding(namespace.embedding);
|
|
1888
|
+
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
1889
|
+
let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
|
|
1890
|
+
|
|
1891
|
+
const json = {
|
|
1892
|
+
id: updated_content._id,
|
|
1893
|
+
type: updated_content.type,
|
|
1894
|
+
source: updated_content.source,
|
|
1895
|
+
content: updated_content.content || "",
|
|
1896
|
+
namespace: updated_content.namespace,
|
|
1897
|
+
webhook: webhook,
|
|
1898
|
+
hybrid: namespace.hybrid,
|
|
1899
|
+
engine: namespace.engine || default_engine,
|
|
1900
|
+
embedding: embedding,
|
|
1901
|
+
...(updated_content.scrape_type && { scrape_type: updated_content.scrape_type }),
|
|
1902
|
+
...(updated_content.scrape_options && { parameters_scrape_type_4: updated_content.scrape_options }),
|
|
1903
|
+
...(updated_content.tags && { tags: updated_content.tags }),
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
winston.debug("json: ", json);
|
|
1907
|
+
|
|
1908
|
+
if (process.env.NODE_ENV === 'test') {
|
|
1909
|
+
return res.status(200).send({ success: true, message: "Schedule scrape skipped in test environment", data: updated_content, schedule_json: json });
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
aiManager.scheduleScrape([json], namespace.hybrid);
|
|
1913
|
+
return res.status(200).send(updated_content);
|
|
1790
1914
|
|
|
1791
1915
|
})
|
|
1792
1916
|
|
|
1917
|
+
// router.put('/:kb_id', async (req, res) => {
|
|
1918
|
+
|
|
1919
|
+
// let kb_id = req.params.kb_id;
|
|
1920
|
+
// winston.verbose("update kb_id " + kb_id);
|
|
1921
|
+
|
|
1922
|
+
// let update = {};
|
|
1923
|
+
|
|
1924
|
+
// if (req.body.name != undefined) {
|
|
1925
|
+
// update.name = req.body.name;
|
|
1926
|
+
// }
|
|
1927
|
+
|
|
1928
|
+
// if (req.body.status != undefined) {
|
|
1929
|
+
// update.status = req.body.status;
|
|
1930
|
+
// }
|
|
1931
|
+
|
|
1932
|
+
// winston.debug("kb update: ", update);
|
|
1933
|
+
|
|
1934
|
+
// KB.findByIdAndUpdate(kb_id, update, { new: true }, (err, savedKb) => {
|
|
1935
|
+
|
|
1936
|
+
// if (err) {
|
|
1937
|
+
// winston.error("KB findByIdAndUpdate error: ", err);
|
|
1938
|
+
// return res.status(500).send({ success: false, error: err });
|
|
1939
|
+
// }
|
|
1940
|
+
|
|
1941
|
+
// if (!savedKb) {
|
|
1942
|
+
// winston.debug("Try to updating a non-existing kb");
|
|
1943
|
+
// return res.status(400).send({ success: false, message: "Content not found" })
|
|
1944
|
+
// }
|
|
1945
|
+
|
|
1946
|
+
// res.status(200).send(savedKb)
|
|
1947
|
+
// })
|
|
1948
|
+
|
|
1949
|
+
// })
|
|
1950
|
+
|
|
1793
1951
|
router.delete('/:kb_id', async (req, res) => {
|
|
1794
1952
|
|
|
1795
1953
|
let project_id = req.projectid;
|