@ontos-ai/knowhere-sdk 0.10.0 → 2.1.1
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/README.md +119 -25
- package/dist/index.d.mts +756 -390
- package/dist/index.d.ts +756 -390
- package/dist/index.js +2057 -56
- package/dist/index.mjs +2053 -55
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -309,8 +309,8 @@ function enrichParseResult(parseResult2, scope) {
|
|
|
309
309
|
}
|
|
310
310
|
return parseResult2;
|
|
311
311
|
}
|
|
312
|
-
function sanitizePath(
|
|
313
|
-
let sanitized =
|
|
312
|
+
function sanitizePath(path4) {
|
|
313
|
+
let sanitized = path4.replace(/^\/+/, "");
|
|
314
314
|
sanitized = sanitized.replace(/\.\.(\/|\\)/g, "");
|
|
315
315
|
sanitized = sanitized.replace(/\\/g, "/");
|
|
316
316
|
return sanitized;
|
|
@@ -696,6 +696,9 @@ var BaseResource = class {
|
|
|
696
696
|
constructor(httpClient) {
|
|
697
697
|
this.httpClient = httpClient;
|
|
698
698
|
}
|
|
699
|
+
endpoint(path4) {
|
|
700
|
+
return `/v2${path4.startsWith("/") ? path4 : `/${path4}`}`;
|
|
701
|
+
}
|
|
699
702
|
};
|
|
700
703
|
|
|
701
704
|
// src/lib/upload.ts
|
|
@@ -746,7 +749,7 @@ async function pollJobStatus(httpClient, jobId, options) {
|
|
|
746
749
|
if (signal?.aborted) {
|
|
747
750
|
throw new Error("Polling aborted");
|
|
748
751
|
}
|
|
749
|
-
const jobResult = await httpClient.get(`/
|
|
752
|
+
const jobResult = await httpClient.get(`/v2/jobs/${jobId}`);
|
|
750
753
|
enrichJobResult(jobResult);
|
|
751
754
|
const elapsed = Date.now() - startTime;
|
|
752
755
|
const elapsedSeconds = Math.floor(elapsed / 1e3);
|
|
@@ -910,17 +913,28 @@ async function saveExpandedParseResult(result, directory) {
|
|
|
910
913
|
if (result.rawZip.length > 0) {
|
|
911
914
|
const didExtractZip = await tryExtractRawZip(result.rawZip, directory);
|
|
912
915
|
if (didExtractZip) {
|
|
916
|
+
await writeExpandedParseResultMetadata(result, directory);
|
|
913
917
|
return directory;
|
|
914
918
|
}
|
|
915
919
|
}
|
|
916
920
|
await fs2.mkdir(directory, { recursive: true });
|
|
921
|
+
await writeExpandedParseResultMetadata(result, directory);
|
|
922
|
+
for (const imageChunk of result.imageChunks) {
|
|
923
|
+
await writeBinaryAsset(directory, imageChunk.filePath, imageChunk.data);
|
|
924
|
+
}
|
|
925
|
+
for (const tableChunk of result.tableChunks) {
|
|
926
|
+
await writeTextAsset(directory, tableChunk.filePath, tableChunk.html);
|
|
927
|
+
}
|
|
928
|
+
return directory;
|
|
929
|
+
}
|
|
930
|
+
async function writeExpandedParseResultMetadata(result, directory) {
|
|
917
931
|
await fs2.writeFile(join(directory, "manifest.json"), JSON.stringify(result.manifest, null, 2));
|
|
918
932
|
if (result.docNav) {
|
|
919
933
|
await fs2.writeFile(join(directory, "doc_nav.json"), JSON.stringify(result.docNav, null, 2));
|
|
920
934
|
}
|
|
921
935
|
await fs2.writeFile(
|
|
922
936
|
join(directory, "chunks.json"),
|
|
923
|
-
JSON.stringify(serializeChunks(result.chunks), null, 2)
|
|
937
|
+
JSON.stringify(keysToSnake(serializeChunks(result.chunks)), null, 2)
|
|
924
938
|
);
|
|
925
939
|
if (result.chunksSlim) {
|
|
926
940
|
await fs2.writeFile(
|
|
@@ -949,13 +963,6 @@ async function saveExpandedParseResult(result, directory) {
|
|
|
949
963
|
if (result.hierarchyViewHtml) {
|
|
950
964
|
await fs2.writeFile(join(directory, "hierarchy_view.html"), result.hierarchyViewHtml);
|
|
951
965
|
}
|
|
952
|
-
for (const imageChunk of result.imageChunks) {
|
|
953
|
-
await writeBinaryAsset(directory, imageChunk.filePath, imageChunk.data);
|
|
954
|
-
}
|
|
955
|
-
for (const tableChunk of result.tableChunks) {
|
|
956
|
-
await writeTextAsset(directory, tableChunk.filePath, tableChunk.html);
|
|
957
|
-
}
|
|
958
|
-
return directory;
|
|
959
966
|
}
|
|
960
967
|
async function tryExtractRawZip(zipBuffer, directory) {
|
|
961
968
|
try {
|
|
@@ -1007,6 +1014,9 @@ function createParseResult(parts) {
|
|
|
1007
1014
|
get tableChunks() {
|
|
1008
1015
|
return chunks.filter((c) => c.type === "table");
|
|
1009
1016
|
},
|
|
1017
|
+
get pageChunks() {
|
|
1018
|
+
return chunks.filter((c) => c.type === "page");
|
|
1019
|
+
},
|
|
1010
1020
|
get jobId() {
|
|
1011
1021
|
return manifest.jobId;
|
|
1012
1022
|
},
|
|
@@ -1084,18 +1094,32 @@ function buildTextChunk(chunkData) {
|
|
|
1084
1094
|
return {
|
|
1085
1095
|
chunkId: chunkData.chunkId ?? "",
|
|
1086
1096
|
type: "text",
|
|
1097
|
+
contentSource: chunkData.contentSource ?? "content",
|
|
1087
1098
|
content: chunkData.content ?? "",
|
|
1088
1099
|
path: chunkData.path ?? "",
|
|
1089
1100
|
metadata: chunkData.metadata ?? {}
|
|
1090
1101
|
};
|
|
1091
1102
|
}
|
|
1103
|
+
function buildPageChunk(chunkData) {
|
|
1104
|
+
const metadata = chunkData.metadata ?? {};
|
|
1105
|
+
return {
|
|
1106
|
+
chunkId: chunkData.chunkId ?? "",
|
|
1107
|
+
type: "page",
|
|
1108
|
+
contentSource: chunkData.contentSource ?? "summary",
|
|
1109
|
+
content: chunkData.content ?? "",
|
|
1110
|
+
path: chunkData.path ?? "",
|
|
1111
|
+
metadata
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1092
1114
|
function buildImageChunk(chunkData, filePath, imageBuffer) {
|
|
1093
1115
|
return {
|
|
1094
1116
|
chunkId: chunkData.chunkId ?? "",
|
|
1095
1117
|
type: "image",
|
|
1118
|
+
contentSource: chunkData.contentSource,
|
|
1096
1119
|
content: chunkData.content ?? "",
|
|
1097
1120
|
path: chunkData.path ?? "",
|
|
1098
1121
|
filePath,
|
|
1122
|
+
assetUrl: chunkData.assetUrl,
|
|
1099
1123
|
data: imageBuffer,
|
|
1100
1124
|
metadata: chunkData.metadata ?? {},
|
|
1101
1125
|
get format() {
|
|
@@ -1110,9 +1134,11 @@ function buildTableChunk(chunkData, filePath, html) {
|
|
|
1110
1134
|
return {
|
|
1111
1135
|
chunkId: chunkData.chunkId ?? "",
|
|
1112
1136
|
type: "table",
|
|
1137
|
+
contentSource: chunkData.contentSource,
|
|
1113
1138
|
content: chunkData.content ?? "",
|
|
1114
1139
|
path: chunkData.path ?? "",
|
|
1115
1140
|
filePath,
|
|
1141
|
+
assetUrl: chunkData.assetUrl,
|
|
1116
1142
|
html,
|
|
1117
1143
|
metadata: chunkData.metadata ?? {},
|
|
1118
1144
|
async save(directory) {
|
|
@@ -1124,6 +1150,9 @@ async function processChunk(zip, chunkData) {
|
|
|
1124
1150
|
if (chunkData.type === "text") {
|
|
1125
1151
|
return buildTextChunk(chunkData);
|
|
1126
1152
|
}
|
|
1153
|
+
if (chunkData.type === "page") {
|
|
1154
|
+
return buildPageChunk(chunkData);
|
|
1155
|
+
}
|
|
1127
1156
|
if (chunkData.type === "image") {
|
|
1128
1157
|
const filePath = getChunkFilePath(chunkData);
|
|
1129
1158
|
if (!filePath) {
|
|
@@ -1156,6 +1185,9 @@ async function processDirectoryChunk(directory, chunkData) {
|
|
|
1156
1185
|
if (chunkData.type === "text") {
|
|
1157
1186
|
return buildTextChunk(chunkData);
|
|
1158
1187
|
}
|
|
1188
|
+
if (chunkData.type === "page") {
|
|
1189
|
+
return buildPageChunk(chunkData);
|
|
1190
|
+
}
|
|
1159
1191
|
if (chunkData.type === "image") {
|
|
1160
1192
|
const filePath = getChunkFilePath(chunkData);
|
|
1161
1193
|
if (!filePath) {
|
|
@@ -1194,12 +1226,14 @@ function serializeChunks(chunks) {
|
|
|
1194
1226
|
const rawChunk = {
|
|
1195
1227
|
chunkId: chunk.chunkId,
|
|
1196
1228
|
type: chunk.type,
|
|
1229
|
+
contentSource: chunk.contentSource,
|
|
1197
1230
|
content: chunk.content,
|
|
1198
1231
|
path: chunk.path,
|
|
1199
1232
|
metadata: chunk.metadata
|
|
1200
1233
|
};
|
|
1201
1234
|
if (chunk.type === "image" || chunk.type === "table") {
|
|
1202
1235
|
rawChunk.filePath = chunk.filePath;
|
|
1236
|
+
rawChunk.assetUrl = chunk.assetUrl;
|
|
1203
1237
|
}
|
|
1204
1238
|
return rawChunk;
|
|
1205
1239
|
})
|
|
@@ -1262,7 +1296,7 @@ var Jobs = class extends BaseResource {
|
|
|
1262
1296
|
* Create a new parsing job
|
|
1263
1297
|
*/
|
|
1264
1298
|
async create(params) {
|
|
1265
|
-
const job = await this.httpClient.post("/
|
|
1299
|
+
const job = await this.httpClient.post(this.endpoint("/jobs"), params);
|
|
1266
1300
|
if (job.uploadUrl) {
|
|
1267
1301
|
this.pendingUploadJobs.set(job.jobId, job);
|
|
1268
1302
|
}
|
|
@@ -1272,7 +1306,7 @@ var Jobs = class extends BaseResource {
|
|
|
1272
1306
|
* Get job status
|
|
1273
1307
|
*/
|
|
1274
1308
|
async get(jobId) {
|
|
1275
|
-
const jobResult = await this.httpClient.get(`/
|
|
1309
|
+
const jobResult = await this.httpClient.get(this.endpoint(`/jobs/${jobId}`));
|
|
1276
1310
|
enrichJobResult(jobResult);
|
|
1277
1311
|
return jobResult;
|
|
1278
1312
|
}
|
|
@@ -1368,7 +1402,7 @@ var Retrieval = class extends BaseResource {
|
|
|
1368
1402
|
* Query published documents.
|
|
1369
1403
|
*/
|
|
1370
1404
|
async query(params) {
|
|
1371
|
-
return this.httpClient.post("/
|
|
1405
|
+
return this.httpClient.post(this.endpoint("/retrieval/query"), params);
|
|
1372
1406
|
}
|
|
1373
1407
|
};
|
|
1374
1408
|
|
|
@@ -1379,7 +1413,7 @@ var Documents = class extends BaseResource {
|
|
|
1379
1413
|
*/
|
|
1380
1414
|
async list(params) {
|
|
1381
1415
|
const response = await this.httpClient.get(
|
|
1382
|
-
"/
|
|
1416
|
+
this.endpoint("/documents"),
|
|
1383
1417
|
this.createDocumentListRequestConfig(params)
|
|
1384
1418
|
);
|
|
1385
1419
|
return this.normalizeDocumentListResponse(response);
|
|
@@ -1388,14 +1422,14 @@ var Documents = class extends BaseResource {
|
|
|
1388
1422
|
* Get one canonical document by ID.
|
|
1389
1423
|
*/
|
|
1390
1424
|
async get(documentId) {
|
|
1391
|
-
return this.httpClient.get(`/
|
|
1425
|
+
return this.httpClient.get(this.endpoint(`/documents/${documentId}`));
|
|
1392
1426
|
}
|
|
1393
1427
|
/**
|
|
1394
1428
|
* List current-revision chunks for one canonical document.
|
|
1395
1429
|
*/
|
|
1396
1430
|
async listChunks(documentId, params) {
|
|
1397
1431
|
return this.httpClient.get(
|
|
1398
|
-
`/
|
|
1432
|
+
this.endpoint(`/documents/${documentId}/chunks`),
|
|
1399
1433
|
this.createChunkListRequestConfig(params)
|
|
1400
1434
|
);
|
|
1401
1435
|
}
|
|
@@ -1404,15 +1438,23 @@ var Documents = class extends BaseResource {
|
|
|
1404
1438
|
*/
|
|
1405
1439
|
async getChunk(documentId, documentChunkId, params) {
|
|
1406
1440
|
return this.httpClient.get(
|
|
1407
|
-
`/
|
|
1441
|
+
this.endpoint(`/documents/${documentId}/chunks/${documentChunkId}`),
|
|
1408
1442
|
this.createChunkGetRequestConfig(params)
|
|
1409
1443
|
);
|
|
1410
1444
|
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Get a signed normalized source-file URL for compatibility and debugging.
|
|
1447
|
+
*/
|
|
1448
|
+
async getPageCitationSource(documentId) {
|
|
1449
|
+
return this.httpClient.get(
|
|
1450
|
+
this.endpoint(`/documents/${documentId}/files/page-citation-source`)
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1411
1453
|
/**
|
|
1412
1454
|
* Archive one canonical document by ID.
|
|
1413
1455
|
*/
|
|
1414
1456
|
async archive(documentId) {
|
|
1415
|
-
return this.httpClient.post(`/
|
|
1457
|
+
return this.httpClient.post(this.endpoint(`/documents/${documentId}/archive`));
|
|
1416
1458
|
}
|
|
1417
1459
|
createDocumentListRequestConfig(params) {
|
|
1418
1460
|
if (!params) {
|
|
@@ -1480,6 +1522,514 @@ var Documents = class extends BaseResource {
|
|
|
1480
1522
|
// src/knowledge/knowledge.ts
|
|
1481
1523
|
import { createHash as createHash2 } from "crypto";
|
|
1482
1524
|
|
|
1525
|
+
// src/storage/asset-storage.ts
|
|
1526
|
+
import JSZip2 from "jszip";
|
|
1527
|
+
var pageCitationAssetsMetadataKey = "pageAssets";
|
|
1528
|
+
var defaultImageContentType = "application/octet-stream";
|
|
1529
|
+
var tableContentType = "text/html; charset=utf-8";
|
|
1530
|
+
var snapshotManifestKey = "manifest/current.json";
|
|
1531
|
+
var snapshotIndexKey = "index.json";
|
|
1532
|
+
var defaultSnapshotChunkPageSize = 200;
|
|
1533
|
+
var snapshotJsonContentType = "application/json; charset=utf-8";
|
|
1534
|
+
async function storeParseResultAssets(result, options) {
|
|
1535
|
+
if (!options) {
|
|
1536
|
+
return {
|
|
1537
|
+
result,
|
|
1538
|
+
assetUrlsByFilePath: {}
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
const normalizedKeyPrefix = normalizeStorageKeyPrefix(options.keyPrefix);
|
|
1542
|
+
const assets = dedupeStorageAssets(await collectStorageAssets(result, normalizedKeyPrefix));
|
|
1543
|
+
const assetUrlsByFilePath = {};
|
|
1544
|
+
for (const asset of assets) {
|
|
1545
|
+
const storedUrl = await storeAsset(asset, options.adapter, options.skipExisting ?? true);
|
|
1546
|
+
if (storedUrl) {
|
|
1547
|
+
assetUrlsByFilePath[asset.sourcePath] = storedUrl;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const normalizedResult = Object.keys(assetUrlsByFilePath).length === 0 ? result : rewriteResultAssetUrls(result, assetUrlsByFilePath);
|
|
1551
|
+
const snapshot = await storeParsedSnapshot({
|
|
1552
|
+
result: normalizedResult,
|
|
1553
|
+
assetUrlsByFilePath,
|
|
1554
|
+
keyPrefix: normalizedKeyPrefix,
|
|
1555
|
+
options
|
|
1556
|
+
});
|
|
1557
|
+
return {
|
|
1558
|
+
result: normalizedResult,
|
|
1559
|
+
assetUrlsByFilePath,
|
|
1560
|
+
snapshot
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
async function storeParsedSnapshot(input) {
|
|
1564
|
+
const pageSize = normalizeSnapshotChunkPageSize(input.options.chunkPageSize);
|
|
1565
|
+
const totalChunks = input.result.chunks.length;
|
|
1566
|
+
const totalPages = Math.max(1, Math.ceil(totalChunks / pageSize));
|
|
1567
|
+
const chunkPageReferences = [];
|
|
1568
|
+
const chunkPageUrlsByPage = {};
|
|
1569
|
+
for (let page = 1; page <= totalPages; page += 1) {
|
|
1570
|
+
const pageChunks = input.result.chunks.slice((page - 1) * pageSize, page * pageSize);
|
|
1571
|
+
const pageKey = `${input.keyPrefix}/chunks/page-${page}.json`;
|
|
1572
|
+
const chunkPage = {
|
|
1573
|
+
version: 1,
|
|
1574
|
+
jobId: input.result.jobId,
|
|
1575
|
+
revisionKey: input.options.revisionKey ?? input.result.jobId,
|
|
1576
|
+
documentId: input.result.documentId,
|
|
1577
|
+
namespace: input.result.namespace,
|
|
1578
|
+
sourceFileName: input.result.manifest.sourceFileName,
|
|
1579
|
+
page,
|
|
1580
|
+
pageSize,
|
|
1581
|
+
total: totalChunks,
|
|
1582
|
+
totalPages,
|
|
1583
|
+
chunks: pageChunks.map(
|
|
1584
|
+
(chunk, index) => toSnapshotChunk(chunk, (page - 1) * pageSize + index + 1)
|
|
1585
|
+
)
|
|
1586
|
+
};
|
|
1587
|
+
const pageUrl = await writeJsonObject({
|
|
1588
|
+
key: pageKey,
|
|
1589
|
+
value: chunkPage,
|
|
1590
|
+
adapter: input.options.adapter
|
|
1591
|
+
});
|
|
1592
|
+
if (pageUrl) chunkPageUrlsByPage[page] = pageUrl;
|
|
1593
|
+
chunkPageReferences.push({
|
|
1594
|
+
page,
|
|
1595
|
+
pageSize,
|
|
1596
|
+
chunkCount: pageChunks.length,
|
|
1597
|
+
key: pageKey,
|
|
1598
|
+
url: pageUrl
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
const manifest = {
|
|
1602
|
+
version: 1,
|
|
1603
|
+
kind: "knowhere-parsed-result-snapshot",
|
|
1604
|
+
jobId: input.result.jobId,
|
|
1605
|
+
revisionKey: input.options.revisionKey ?? input.result.jobId,
|
|
1606
|
+
documentId: input.result.documentId,
|
|
1607
|
+
namespace: input.result.namespace,
|
|
1608
|
+
sourceFileName: input.result.manifest.sourceFileName,
|
|
1609
|
+
totalChunks,
|
|
1610
|
+
typeCounts: countSnapshotChunkTypes(input.result.chunks),
|
|
1611
|
+
chunkPageSize: pageSize,
|
|
1612
|
+
chunkPages: chunkPageReferences,
|
|
1613
|
+
assetUrlsByFilePath: input.assetUrlsByFilePath,
|
|
1614
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1615
|
+
};
|
|
1616
|
+
const manifestKey = `${input.keyPrefix}/${snapshotManifestKey}`;
|
|
1617
|
+
const indexKey = `${input.keyPrefix}/${snapshotIndexKey}`;
|
|
1618
|
+
const manifestUrl = await writeJsonObject({
|
|
1619
|
+
key: manifestKey,
|
|
1620
|
+
value: manifest,
|
|
1621
|
+
adapter: input.options.adapter
|
|
1622
|
+
});
|
|
1623
|
+
const indexUrl = await writeJsonObject({
|
|
1624
|
+
key: indexKey,
|
|
1625
|
+
value: manifest,
|
|
1626
|
+
adapter: input.options.adapter
|
|
1627
|
+
});
|
|
1628
|
+
return {
|
|
1629
|
+
manifest,
|
|
1630
|
+
manifestKey,
|
|
1631
|
+
manifestUrl,
|
|
1632
|
+
indexKey,
|
|
1633
|
+
indexUrl,
|
|
1634
|
+
chunkPageUrlsByPage
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function countSnapshotChunkTypes(chunks) {
|
|
1638
|
+
return chunks.reduce(
|
|
1639
|
+
(counts, chunk) => {
|
|
1640
|
+
counts[chunk.type] += 1;
|
|
1641
|
+
return counts;
|
|
1642
|
+
},
|
|
1643
|
+
{ text: 0, image: 0, table: 0, page: 0 }
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
async function writeJsonObject(input) {
|
|
1647
|
+
const result = await input.adapter.writeObject({
|
|
1648
|
+
key: input.key,
|
|
1649
|
+
body: Buffer.from(JSON.stringify(input.value, null, 2), "utf8"),
|
|
1650
|
+
contentType: snapshotJsonContentType,
|
|
1651
|
+
metadata: {
|
|
1652
|
+
kind: "knowhere-parsed-result-snapshot"
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
return result.url ?? await input.adapter.getObjectUrl?.(result.key) ?? void 0;
|
|
1656
|
+
}
|
|
1657
|
+
function normalizeSnapshotChunkPageSize(value) {
|
|
1658
|
+
if (!Number.isInteger(value) || value === void 0) return defaultSnapshotChunkPageSize;
|
|
1659
|
+
return Math.min(Math.max(value, 1), 1e3);
|
|
1660
|
+
}
|
|
1661
|
+
function toSnapshotChunk(chunk, position) {
|
|
1662
|
+
const filePath = getSnapshotChunkFilePath(chunk);
|
|
1663
|
+
return {
|
|
1664
|
+
id: chunk.chunkId,
|
|
1665
|
+
chunkId: chunk.chunkId,
|
|
1666
|
+
chunkType: chunk.type,
|
|
1667
|
+
contentSource: chunk.contentSource,
|
|
1668
|
+
content: chunk.content,
|
|
1669
|
+
sectionPath: normalizeSnapshotSectionPath(chunk.path),
|
|
1670
|
+
sourceChunkPath: chunk.path,
|
|
1671
|
+
filePath,
|
|
1672
|
+
sortOrder: position,
|
|
1673
|
+
metadata: cloneChunkMetadata(chunk.metadata),
|
|
1674
|
+
assetUrl: getSnapshotChunkAssetUrl(chunk)
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
function normalizeSnapshotSectionPath(path4) {
|
|
1678
|
+
return path4.length > 0 ? path4 : void 0;
|
|
1679
|
+
}
|
|
1680
|
+
function getSnapshotChunkFilePath(chunk) {
|
|
1681
|
+
if (chunk.type === "image" || chunk.type === "table") {
|
|
1682
|
+
return chunk.filePath;
|
|
1683
|
+
}
|
|
1684
|
+
const filePath = chunk.metadata.filePath;
|
|
1685
|
+
return typeof filePath === "string" ? filePath : void 0;
|
|
1686
|
+
}
|
|
1687
|
+
function getSnapshotChunkAssetUrl(chunk) {
|
|
1688
|
+
return chunk.type === "image" || chunk.type === "table" ? chunk.assetUrl : void 0;
|
|
1689
|
+
}
|
|
1690
|
+
function cloneChunkMetadata(metadata) {
|
|
1691
|
+
return { ...metadata };
|
|
1692
|
+
}
|
|
1693
|
+
async function collectStorageAssets(result, keyPrefix) {
|
|
1694
|
+
const resultZip = await loadResultZipForPageCitationAssets(result);
|
|
1695
|
+
return [
|
|
1696
|
+
...result.imageChunks.flatMap((chunk) => collectImageStorageAsset(chunk, keyPrefix)),
|
|
1697
|
+
...result.tableChunks.flatMap((chunk) => collectTableStorageAsset(chunk, keyPrefix)),
|
|
1698
|
+
...resultZip ? await collectPageCitationStorageAssets(result.pageChunks, keyPrefix, resultZip) : []
|
|
1699
|
+
];
|
|
1700
|
+
}
|
|
1701
|
+
function collectImageStorageAsset(chunk, keyPrefix) {
|
|
1702
|
+
const sourcePath = normalizeResultAssetPath(chunk.filePath);
|
|
1703
|
+
if (!sourcePath) return [];
|
|
1704
|
+
return [
|
|
1705
|
+
{
|
|
1706
|
+
sourcePath,
|
|
1707
|
+
storageKey: `${keyPrefix}/${sourcePath}`,
|
|
1708
|
+
body: chunk.data,
|
|
1709
|
+
contentType: getImageContentType(chunk.filePath),
|
|
1710
|
+
metadata: {
|
|
1711
|
+
chunkId: chunk.chunkId,
|
|
1712
|
+
chunkType: chunk.type,
|
|
1713
|
+
sourcePath
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
];
|
|
1717
|
+
}
|
|
1718
|
+
function collectTableStorageAsset(chunk, keyPrefix) {
|
|
1719
|
+
const sourcePath = normalizeResultAssetPath(chunk.filePath);
|
|
1720
|
+
if (!sourcePath) return [];
|
|
1721
|
+
return [
|
|
1722
|
+
{
|
|
1723
|
+
sourcePath,
|
|
1724
|
+
storageKey: `${keyPrefix}/${sourcePath}`,
|
|
1725
|
+
body: Buffer.from(chunk.html, "utf8"),
|
|
1726
|
+
contentType: tableContentType,
|
|
1727
|
+
metadata: {
|
|
1728
|
+
chunkId: chunk.chunkId,
|
|
1729
|
+
chunkType: chunk.type,
|
|
1730
|
+
sourcePath
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
];
|
|
1734
|
+
}
|
|
1735
|
+
async function collectPageCitationStorageAssets(chunks, keyPrefix, resultZip) {
|
|
1736
|
+
const assets = [];
|
|
1737
|
+
for (const chunk of chunks) {
|
|
1738
|
+
const pageAssets = parsePageCitationAssets(chunk.metadata[pageCitationAssetsMetadataKey]);
|
|
1739
|
+
for (const pageAsset of pageAssets) {
|
|
1740
|
+
const sourcePath = normalizeResultAssetPath(pageAsset.artifactRef);
|
|
1741
|
+
if (!sourcePath) continue;
|
|
1742
|
+
const zipEntry = resultZip.file(sourcePath);
|
|
1743
|
+
if (!zipEntry) continue;
|
|
1744
|
+
assets.push({
|
|
1745
|
+
sourcePath,
|
|
1746
|
+
storageKey: `${keyPrefix}/${sourcePath}`,
|
|
1747
|
+
body: await zipEntry.async("uint8array"),
|
|
1748
|
+
contentType: pageAsset.contentType,
|
|
1749
|
+
metadata: getPageCitationAssetMetadata(chunk, pageAsset, sourcePath)
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return assets;
|
|
1754
|
+
}
|
|
1755
|
+
function getPageCitationAssetMetadata(chunk, pageAsset, sourcePath) {
|
|
1756
|
+
return {
|
|
1757
|
+
chunkId: chunk.chunkId,
|
|
1758
|
+
chunkType: chunk.type,
|
|
1759
|
+
pageNum: String(pageAsset.pageNum),
|
|
1760
|
+
sourcePath
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
async function loadResultZipForPageCitationAssets(result) {
|
|
1764
|
+
if (!hasPageCitationAssetReferences(result) || result.rawZip.length === 0) {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
try {
|
|
1768
|
+
return await JSZip2.loadAsync(result.rawZip);
|
|
1769
|
+
} catch (cause) {
|
|
1770
|
+
throw new Error("Unable to read page citation assets from the Knowhere result ZIP.", {
|
|
1771
|
+
cause
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
function hasPageCitationAssetReferences(result) {
|
|
1776
|
+
return result.pageChunks.some(
|
|
1777
|
+
(chunk) => parsePageCitationAssets(chunk.metadata[pageCitationAssetsMetadataKey]).length > 0
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
function dedupeStorageAssets(assets) {
|
|
1781
|
+
const assetsBySourcePath = /* @__PURE__ */ new Map();
|
|
1782
|
+
for (const asset of assets) {
|
|
1783
|
+
if (!assetsBySourcePath.has(asset.sourcePath)) {
|
|
1784
|
+
assetsBySourcePath.set(asset.sourcePath, asset);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return [...assetsBySourcePath.values()];
|
|
1788
|
+
}
|
|
1789
|
+
async function storeAsset(asset, adapter, skipExisting) {
|
|
1790
|
+
if (skipExisting && adapter.headObject) {
|
|
1791
|
+
const existing = await adapter.headObject(asset.storageKey);
|
|
1792
|
+
if (existing) {
|
|
1793
|
+
const existingUrl = existing.url ?? await adapter.getObjectUrl?.(asset.storageKey);
|
|
1794
|
+
if (existingUrl) return existingUrl;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
const writeResult = await adapter.writeObject({
|
|
1798
|
+
key: asset.storageKey,
|
|
1799
|
+
body: asset.body,
|
|
1800
|
+
contentType: asset.contentType,
|
|
1801
|
+
metadata: asset.metadata
|
|
1802
|
+
});
|
|
1803
|
+
return writeResult.url ?? await adapter.getObjectUrl?.(writeResult.key) ?? null;
|
|
1804
|
+
}
|
|
1805
|
+
function rewriteResultAssetUrls(result, assetUrlsByFilePath) {
|
|
1806
|
+
const chunks = result.chunks.map((chunk) => rewriteChunkAssetUrls(chunk, assetUrlsByFilePath));
|
|
1807
|
+
result.chunks.splice(0, result.chunks.length, ...chunks);
|
|
1808
|
+
refreshStaticChunkCollections(result);
|
|
1809
|
+
return result;
|
|
1810
|
+
}
|
|
1811
|
+
function rewriteChunkAssetUrls(chunk, assetUrlsByPath) {
|
|
1812
|
+
if (chunk.type === "image" || chunk.type === "table") {
|
|
1813
|
+
const sourcePath = normalizeResultAssetPath(chunk.filePath);
|
|
1814
|
+
const assetUrl = sourcePath ? assetUrlsByPath[sourcePath] : void 0;
|
|
1815
|
+
if (!assetUrl) return chunk;
|
|
1816
|
+
return {
|
|
1817
|
+
...chunk,
|
|
1818
|
+
assetUrl
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
if (chunk.type === "page") {
|
|
1822
|
+
const pageAssets = parsePageCitationAssets(chunk.metadata[pageCitationAssetsMetadataKey]);
|
|
1823
|
+
if (pageAssets.length === 0) return chunk;
|
|
1824
|
+
const rewrittenPageAssets = pageAssets.map((pageAsset) => {
|
|
1825
|
+
const sourcePath = normalizeResultAssetPath(pageAsset.artifactRef);
|
|
1826
|
+
const assetUrl = sourcePath ? assetUrlsByPath[sourcePath] : void 0;
|
|
1827
|
+
return assetUrl ? { ...pageAsset, assetUrl } : pageAsset;
|
|
1828
|
+
});
|
|
1829
|
+
return {
|
|
1830
|
+
...chunk,
|
|
1831
|
+
metadata: {
|
|
1832
|
+
...chunk.metadata,
|
|
1833
|
+
[pageCitationAssetsMetadataKey]: rewrittenPageAssets
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
return chunk;
|
|
1838
|
+
}
|
|
1839
|
+
function parsePageCitationAssets(value) {
|
|
1840
|
+
if (!Array.isArray(value)) return [];
|
|
1841
|
+
return value.flatMap((item) => {
|
|
1842
|
+
if (!isRecord(item)) return [];
|
|
1843
|
+
const pageNum = item.pageNum;
|
|
1844
|
+
const artifactRef = item.artifactRef;
|
|
1845
|
+
const contentType = item.contentType;
|
|
1846
|
+
const source = item.source;
|
|
1847
|
+
if (typeof pageNum !== "number" || !Number.isSafeInteger(pageNum) || pageNum <= 0) return [];
|
|
1848
|
+
if (typeof artifactRef !== "string") return [];
|
|
1849
|
+
if (contentType !== "image/png" && contentType !== "image/jpeg") return [];
|
|
1850
|
+
if (source !== "knowhere-rendered-page-citation-source") return [];
|
|
1851
|
+
return [
|
|
1852
|
+
{
|
|
1853
|
+
pageNum,
|
|
1854
|
+
artifactRef,
|
|
1855
|
+
assetUrl: typeof item.assetUrl === "string" ? item.assetUrl : void 0,
|
|
1856
|
+
contentType,
|
|
1857
|
+
width: typeof item.width === "number" ? item.width : void 0,
|
|
1858
|
+
height: typeof item.height === "number" ? item.height : void 0,
|
|
1859
|
+
source
|
|
1860
|
+
}
|
|
1861
|
+
];
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
function normalizeStorageKeyPrefix(value) {
|
|
1865
|
+
const normalized = normalizeStoragePath(value, { allowDotPrefix: false });
|
|
1866
|
+
if (!normalized) {
|
|
1867
|
+
throw new Error("storageAdapter keyPrefix must be a non-empty relative POSIX path.");
|
|
1868
|
+
}
|
|
1869
|
+
return normalized;
|
|
1870
|
+
}
|
|
1871
|
+
function normalizeResultAssetPath(value) {
|
|
1872
|
+
return normalizeStoragePath(value, { allowDotPrefix: true });
|
|
1873
|
+
}
|
|
1874
|
+
function normalizeStoragePath(value, options) {
|
|
1875
|
+
if (!value) return null;
|
|
1876
|
+
const trimmed = value.trim();
|
|
1877
|
+
if (trimmed.length === 0 || trimmed.includes("\0")) return null;
|
|
1878
|
+
if (trimmed.startsWith("/") || /^[A-Za-z]:/.test(trimmed)) return null;
|
|
1879
|
+
const normalized = options.allowDotPrefix ? trimmed.replaceAll("\\", "/").replace(/^\.\/+/, "") : trimmed.replaceAll("\\", "/");
|
|
1880
|
+
if (normalized.length === 0) return null;
|
|
1881
|
+
const parts = normalized.split("/");
|
|
1882
|
+
if (parts.some((part) => part.length === 0 || part === "." || part === "..")) return null;
|
|
1883
|
+
return parts.join("/");
|
|
1884
|
+
}
|
|
1885
|
+
function refreshStaticChunkCollections(result) {
|
|
1886
|
+
replaceStaticChunkCollection(
|
|
1887
|
+
result,
|
|
1888
|
+
"textChunks",
|
|
1889
|
+
result.chunks.filter((chunk) => chunk.type === "text")
|
|
1890
|
+
);
|
|
1891
|
+
replaceStaticChunkCollection(
|
|
1892
|
+
result,
|
|
1893
|
+
"imageChunks",
|
|
1894
|
+
result.chunks.filter((chunk) => chunk.type === "image")
|
|
1895
|
+
);
|
|
1896
|
+
replaceStaticChunkCollection(
|
|
1897
|
+
result,
|
|
1898
|
+
"tableChunks",
|
|
1899
|
+
result.chunks.filter((chunk) => chunk.type === "table")
|
|
1900
|
+
);
|
|
1901
|
+
replaceStaticChunkCollection(
|
|
1902
|
+
result,
|
|
1903
|
+
"pageChunks",
|
|
1904
|
+
result.chunks.filter((chunk) => chunk.type === "page")
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
function replaceStaticChunkCollection(result, property, value) {
|
|
1908
|
+
const descriptor = Object.getOwnPropertyDescriptor(result, property);
|
|
1909
|
+
if (!descriptor || descriptor.get || descriptor.writable !== true) return;
|
|
1910
|
+
Object.defineProperty(result, property, {
|
|
1911
|
+
...descriptor,
|
|
1912
|
+
value
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
function getImageContentType(filePath) {
|
|
1916
|
+
const extension = filePath.split(".").at(-1)?.toLowerCase();
|
|
1917
|
+
if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
|
|
1918
|
+
if (extension === "png") return "image/png";
|
|
1919
|
+
if (extension === "webp") return "image/webp";
|
|
1920
|
+
if (extension === "gif") return "image/gif";
|
|
1921
|
+
if (extension === "svg") return "image/svg+xml";
|
|
1922
|
+
return defaultImageContentType;
|
|
1923
|
+
}
|
|
1924
|
+
function isRecord(value) {
|
|
1925
|
+
return typeof value === "object" && value !== null;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// src/storage/parsed-document-storage.ts
|
|
1929
|
+
var parsedStorageKeyPrefix = "parsed";
|
|
1930
|
+
var manifestStoragePath = "manifest/current.json";
|
|
1931
|
+
var indexStoragePath = "index.json";
|
|
1932
|
+
var chunkPagePathPattern = /^chunks\/page-(\d+)\.json$/;
|
|
1933
|
+
async function syncParseResultToParsedDocumentStorage(params) {
|
|
1934
|
+
const documentId = params.documentId ?? params.result.documentId ?? params.result.jobId;
|
|
1935
|
+
const revisionKey = params.revisionKey ?? params.result.jobId;
|
|
1936
|
+
const adapter = createParsedDocumentObjectStorageAdapter({
|
|
1937
|
+
storage: params.storage,
|
|
1938
|
+
documentId,
|
|
1939
|
+
revisionKey
|
|
1940
|
+
});
|
|
1941
|
+
const stored = await storeParseResultAssets(params.result, {
|
|
1942
|
+
adapter,
|
|
1943
|
+
keyPrefix: parsedStorageKeyPrefix,
|
|
1944
|
+
chunkPageSize: params.chunkPageSize,
|
|
1945
|
+
revisionKey
|
|
1946
|
+
});
|
|
1947
|
+
return {
|
|
1948
|
+
result: stored.result,
|
|
1949
|
+
snapshot: stored.snapshot,
|
|
1950
|
+
documentId,
|
|
1951
|
+
revisionKey
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
function createParsedDocumentObjectStorageAdapter(params) {
|
|
1955
|
+
return {
|
|
1956
|
+
writeObject: (input) => writeParsedStorageObject(params, input),
|
|
1957
|
+
headObject: (key) => headParsedStorageObject(params, key),
|
|
1958
|
+
getObjectUrl: (key) => getParsedStorageObjectUrl(params, key)
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
async function writeParsedStorageObject(params, input) {
|
|
1962
|
+
const storagePath = stripParsedStorageKeyPrefix(input.key);
|
|
1963
|
+
if (storagePath === manifestStoragePath) {
|
|
1964
|
+
const manifest = JSON.parse(
|
|
1965
|
+
Buffer.from(input.body).toString("utf8")
|
|
1966
|
+
);
|
|
1967
|
+
await params.storage.writeManifest({
|
|
1968
|
+
documentId: params.documentId,
|
|
1969
|
+
revisionKey: params.revisionKey,
|
|
1970
|
+
manifest
|
|
1971
|
+
});
|
|
1972
|
+
return { key: input.key };
|
|
1973
|
+
}
|
|
1974
|
+
if (storagePath === indexStoragePath) {
|
|
1975
|
+
return { key: input.key };
|
|
1976
|
+
}
|
|
1977
|
+
const chunkPageMatch = storagePath.match(chunkPagePathPattern);
|
|
1978
|
+
if (chunkPageMatch) {
|
|
1979
|
+
const page = JSON.parse(
|
|
1980
|
+
Buffer.from(input.body).toString("utf8")
|
|
1981
|
+
);
|
|
1982
|
+
await params.storage.writeChunkPage({
|
|
1983
|
+
documentId: params.documentId,
|
|
1984
|
+
revisionKey: params.revisionKey,
|
|
1985
|
+
page
|
|
1986
|
+
});
|
|
1987
|
+
return { key: input.key };
|
|
1988
|
+
}
|
|
1989
|
+
const stored = await params.storage.writeAsset({
|
|
1990
|
+
documentId: params.documentId,
|
|
1991
|
+
revisionKey: params.revisionKey,
|
|
1992
|
+
sourcePath: storagePath,
|
|
1993
|
+
body: input.body,
|
|
1994
|
+
contentType: input.contentType,
|
|
1995
|
+
metadata: input.metadata
|
|
1996
|
+
});
|
|
1997
|
+
return {
|
|
1998
|
+
key: input.key,
|
|
1999
|
+
url: stored.url
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
async function headParsedStorageObject(params, key) {
|
|
2003
|
+
const storagePath = stripParsedStorageKeyPrefix(key);
|
|
2004
|
+
if (storagePath === manifestStoragePath || storagePath === indexStoragePath || chunkPagePathPattern.test(storagePath)) {
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
const url = await params.storage.getAssetUrl({
|
|
2008
|
+
documentId: params.documentId,
|
|
2009
|
+
revisionKey: params.revisionKey,
|
|
2010
|
+
sourcePath: storagePath
|
|
2011
|
+
});
|
|
2012
|
+
return url ? {
|
|
2013
|
+
key,
|
|
2014
|
+
url
|
|
2015
|
+
} : null;
|
|
2016
|
+
}
|
|
2017
|
+
async function getParsedStorageObjectUrl(params, key) {
|
|
2018
|
+
const storagePath = stripParsedStorageKeyPrefix(key);
|
|
2019
|
+
if (storagePath === manifestStoragePath || storagePath === indexStoragePath || chunkPagePathPattern.test(storagePath)) {
|
|
2020
|
+
return null;
|
|
2021
|
+
}
|
|
2022
|
+
return params.storage.getAssetUrl({
|
|
2023
|
+
documentId: params.documentId,
|
|
2024
|
+
revisionKey: params.revisionKey,
|
|
2025
|
+
sourcePath: storagePath
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
function stripParsedStorageKeyPrefix(key) {
|
|
2029
|
+
const prefix = `${parsedStorageKeyPrefix}/`;
|
|
2030
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1483
2033
|
// src/knowledge/local-store.ts
|
|
1484
2034
|
import { createHash } from "crypto";
|
|
1485
2035
|
import os from "os";
|
|
@@ -1674,7 +2224,7 @@ function countChunkTypes(result) {
|
|
|
1674
2224
|
counts[chunk.type] += 1;
|
|
1675
2225
|
return counts;
|
|
1676
2226
|
},
|
|
1677
|
-
{ text: 0, image: 0, table: 0 }
|
|
2227
|
+
{ text: 0, image: 0, table: 0, page: 0 }
|
|
1678
2228
|
);
|
|
1679
2229
|
}
|
|
1680
2230
|
function toLocalKnowledgeDocument(stored) {
|
|
@@ -1705,27 +2255,62 @@ function isMissingFileError2(error) {
|
|
|
1705
2255
|
// src/knowledge/knowledge.ts
|
|
1706
2256
|
var DEFAULT_READ_LIMIT = 12;
|
|
1707
2257
|
var MAX_READ_LIMIT = 40;
|
|
2258
|
+
var DEFAULT_PAGE_SIZE = 20;
|
|
2259
|
+
var MAX_PAGE_SIZE = 200;
|
|
1708
2260
|
var DEFAULT_GREP_LIMIT = 20;
|
|
1709
2261
|
var MAX_GREP_LIMIT = 50;
|
|
1710
2262
|
var DEFAULT_CONTEXT_CHARS = 80;
|
|
2263
|
+
var DEFAULT_REMOTE_SCAN_PAGE_SIZE = 100;
|
|
2264
|
+
var DEFAULT_MAX_SYNC_PAGES = 10;
|
|
2265
|
+
var DEFAULT_MAX_SYNC_ASSETS = 20;
|
|
2266
|
+
var DEFAULT_SYNC_DEADLINE_MS = 8e3;
|
|
2267
|
+
var DEFAULT_GREP_MAX_PAGES = 50;
|
|
2268
|
+
var DEFAULT_GREP_DEADLINE_MS = 8e3;
|
|
2269
|
+
var DEFAULT_OUTLINE_MAX_PAGES = 50;
|
|
2270
|
+
var DEFAULT_OUTLINE_DEADLINE_MS = 8e3;
|
|
1711
2271
|
var SAFE_LOCAL_DOCUMENT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1712
2272
|
var REMOTE_DOCUMENT_ID_PATTERN = /^doc[_-]/;
|
|
1713
2273
|
var Knowledge = class _Knowledge {
|
|
1714
2274
|
client;
|
|
1715
2275
|
store;
|
|
2276
|
+
cacheDirectory;
|
|
2277
|
+
parsedStorageConfig;
|
|
1716
2278
|
constructor(client, options) {
|
|
1717
2279
|
this.client = client;
|
|
2280
|
+
this.cacheDirectory = options?.cacheDirectory;
|
|
2281
|
+
this.parsedStorageConfig = options?.parsedStorage;
|
|
1718
2282
|
this.store = new LocalKnowledgeStore(options?.cacheDirectory);
|
|
1719
2283
|
}
|
|
1720
2284
|
withCacheDirectory(cacheDirectory) {
|
|
1721
|
-
return new _Knowledge(this.client, {
|
|
2285
|
+
return new _Knowledge(this.client, {
|
|
2286
|
+
cacheDirectory,
|
|
2287
|
+
parsedStorage: this.parsedStorageConfig
|
|
2288
|
+
});
|
|
1722
2289
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
2290
|
+
withParsedStorage(parsedStorage) {
|
|
2291
|
+
return new _Knowledge(this.client, {
|
|
2292
|
+
cacheDirectory: this.cacheDirectory,
|
|
2293
|
+
parsedStorage
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
async parseToLocalCache(params) {
|
|
2297
|
+
const loadedResult = params.storageAdapter ? await this.parseWithoutClientStorageAdapter(params) : await this.client.parse(params);
|
|
2298
|
+
const storedAssets = await storeParseResultAssets(loadedResult, params.storageAdapter);
|
|
2299
|
+
const syncedStorage = await this.syncParseResultToConfiguredStorage(storedAssets.result);
|
|
2300
|
+
const result = syncedStorage.result;
|
|
1725
2301
|
const document = await this.store.saveResult(result, {
|
|
1726
2302
|
localDocumentId: params.localDocumentId
|
|
1727
2303
|
});
|
|
1728
|
-
return {
|
|
2304
|
+
return {
|
|
2305
|
+
document,
|
|
2306
|
+
result,
|
|
2307
|
+
assetUrlsByFilePath: storedAssets.assetUrlsByFilePath,
|
|
2308
|
+
parsedSnapshot: storedAssets.snapshot
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
/** @deprecated Use parseToLocalCache for local cache imports or the top-level client.parse for server-safe parsing. */
|
|
2312
|
+
async parse(params) {
|
|
2313
|
+
return this.parseToLocalCache(params);
|
|
1729
2314
|
}
|
|
1730
2315
|
async startParse(params) {
|
|
1731
2316
|
const job = await this.client.startParse(params);
|
|
@@ -1756,14 +2341,100 @@ var Knowledge = class _Knowledge {
|
|
|
1756
2341
|
results
|
|
1757
2342
|
};
|
|
1758
2343
|
}
|
|
1759
|
-
async
|
|
1760
|
-
const
|
|
2344
|
+
async loadJobResult(params) {
|
|
2345
|
+
const loadedResult = await this.client.jobs.load(params.jobId, {
|
|
1761
2346
|
verifyChecksum: params.verifyChecksum
|
|
1762
2347
|
});
|
|
2348
|
+
const storedAssets = await storeParseResultAssets(loadedResult, params.storageAdapter);
|
|
2349
|
+
const syncedStorage = await this.syncParseResultToConfiguredStorage(storedAssets.result);
|
|
2350
|
+
const result = syncedStorage.result;
|
|
2351
|
+
return {
|
|
2352
|
+
result,
|
|
2353
|
+
assetUrlsByFilePath: storedAssets.assetUrlsByFilePath,
|
|
2354
|
+
parsedSnapshot: syncedStorage.snapshot ?? storedAssets.snapshot
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
async importJobResult(params) {
|
|
2358
|
+
const stored = await this.loadJobResult(params);
|
|
2359
|
+
const result = stored.result;
|
|
1763
2360
|
const document = await this.store.saveResult(result, {
|
|
1764
2361
|
localDocumentId: params.localDocumentId
|
|
1765
2362
|
});
|
|
1766
|
-
return {
|
|
2363
|
+
return {
|
|
2364
|
+
document,
|
|
2365
|
+
result,
|
|
2366
|
+
assetUrlsByFilePath: stored.assetUrlsByFilePath,
|
|
2367
|
+
parsedSnapshot: stored.parsedSnapshot
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
/** @deprecated Use importJobResult for local cache imports or loadJobResult for server-safe loads. */
|
|
2371
|
+
async cacheJobResult(params) {
|
|
2372
|
+
return this.importJobResult(params);
|
|
2373
|
+
}
|
|
2374
|
+
async syncParsedDocument(params) {
|
|
2375
|
+
const normalized = normalizeDocumentReference(params);
|
|
2376
|
+
const parsedStorageConfig = this.requireParsedStorageConfig();
|
|
2377
|
+
if (normalized.documentId) {
|
|
2378
|
+
return this.syncRemoteDocumentToParsedStorage({
|
|
2379
|
+
documentId: normalized.documentId,
|
|
2380
|
+
revisionKey: params.revisionKey,
|
|
2381
|
+
parsedStorageConfig
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
if (normalized.jobId) {
|
|
2385
|
+
const loadedResult = await this.client.jobs.load(normalized.jobId);
|
|
2386
|
+
const synced2 = await syncParseResultToParsedDocumentStorage({
|
|
2387
|
+
result: loadedResult,
|
|
2388
|
+
storage: parsedStorageConfig.storage,
|
|
2389
|
+
documentId: loadedResult.documentId ?? normalized.localDocumentId ?? normalized.jobId,
|
|
2390
|
+
revisionKey: params.revisionKey ?? normalized.jobId,
|
|
2391
|
+
chunkPageSize: parsedStorageConfig.limits?.chunkPageSize
|
|
2392
|
+
});
|
|
2393
|
+
return {
|
|
2394
|
+
documentId: synced2.documentId,
|
|
2395
|
+
revisionKey: synced2.revisionKey,
|
|
2396
|
+
completed: true,
|
|
2397
|
+
manifest: synced2.snapshot?.manifest
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
if (!normalized.localDocumentId) {
|
|
2401
|
+
throw new ValidationError("localDocumentId, documentId, or jobId is required");
|
|
2402
|
+
}
|
|
2403
|
+
const { result } = await this.store.loadResult(normalized.localDocumentId);
|
|
2404
|
+
const synced = await syncParseResultToParsedDocumentStorage({
|
|
2405
|
+
result,
|
|
2406
|
+
storage: parsedStorageConfig.storage,
|
|
2407
|
+
documentId: result.documentId ?? normalized.localDocumentId,
|
|
2408
|
+
revisionKey: params.revisionKey ?? result.jobId,
|
|
2409
|
+
chunkPageSize: parsedStorageConfig.limits?.chunkPageSize
|
|
2410
|
+
});
|
|
2411
|
+
return {
|
|
2412
|
+
documentId: synced.documentId,
|
|
2413
|
+
revisionKey: synced.revisionKey,
|
|
2414
|
+
completed: true,
|
|
2415
|
+
manifest: synced.snapshot?.manifest
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
async syncParseResultToConfiguredStorage(result) {
|
|
2419
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
2420
|
+
if (!parsedStorageConfig) {
|
|
2421
|
+
return { result };
|
|
2422
|
+
}
|
|
2423
|
+
const synced = await syncParseResultToParsedDocumentStorage({
|
|
2424
|
+
result,
|
|
2425
|
+
storage: parsedStorageConfig.storage,
|
|
2426
|
+
chunkPageSize: parsedStorageConfig.limits?.chunkPageSize
|
|
2427
|
+
});
|
|
2428
|
+
return {
|
|
2429
|
+
result: synced.result,
|
|
2430
|
+
snapshot: synced.snapshot
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
async parseWithoutClientStorageAdapter(params) {
|
|
2434
|
+
const parseParams = { ...params };
|
|
2435
|
+
delete parseParams.localDocumentId;
|
|
2436
|
+
delete parseParams.storageAdapter;
|
|
2437
|
+
return this.client.parse(parseParams);
|
|
1767
2438
|
}
|
|
1768
2439
|
async cacheDocument(params) {
|
|
1769
2440
|
if (!params.documentId) {
|
|
@@ -1773,7 +2444,7 @@ var Knowledge = class _Knowledge {
|
|
|
1773
2444
|
params.documentId
|
|
1774
2445
|
);
|
|
1775
2446
|
const jobId = await this.getPublishedDocumentJobId(params.documentId);
|
|
1776
|
-
return this.
|
|
2447
|
+
return this.importJobResult({
|
|
1777
2448
|
jobId,
|
|
1778
2449
|
localDocumentId: params.localDocumentId ?? existing?.localDocumentId ?? createLocalDocumentIdForRemote(params.documentId)
|
|
1779
2450
|
});
|
|
@@ -1810,7 +2481,7 @@ var Knowledge = class _Knowledge {
|
|
|
1810
2481
|
};
|
|
1811
2482
|
}
|
|
1812
2483
|
try {
|
|
1813
|
-
const cached = await this.
|
|
2484
|
+
const cached = await this.importJobResult({
|
|
1814
2485
|
jobId,
|
|
1815
2486
|
localDocumentId: trackedJob.localDocumentId
|
|
1816
2487
|
});
|
|
@@ -1835,6 +2506,13 @@ var Knowledge = class _Knowledge {
|
|
|
1835
2506
|
return this.store.listDocuments();
|
|
1836
2507
|
}
|
|
1837
2508
|
async getDocumentOutline(reference) {
|
|
2509
|
+
const normalized = normalizeDocumentReference(reference);
|
|
2510
|
+
if (normalized.documentId) {
|
|
2511
|
+
return this.getRemoteDocumentOutline(normalized.documentId, normalized.revisionKey);
|
|
2512
|
+
}
|
|
2513
|
+
if (normalized.localDocumentId && looksLikeRemoteDocumentId(normalized.localDocumentId) && !await this.store.getDocument(normalized.localDocumentId)) {
|
|
2514
|
+
return this.getRemoteDocumentOutline(normalized.localDocumentId, normalized.revisionKey);
|
|
2515
|
+
}
|
|
1838
2516
|
const { document, result } = await this.loadReadableResult(reference);
|
|
1839
2517
|
const chunks = indexChunks(result);
|
|
1840
2518
|
const sections = buildFlatSections(result, chunks);
|
|
@@ -1850,6 +2528,14 @@ var Knowledge = class _Knowledge {
|
|
|
1850
2528
|
};
|
|
1851
2529
|
}
|
|
1852
2530
|
async readChunks(params) {
|
|
2531
|
+
validateReadParams(params);
|
|
2532
|
+
const normalized = normalizeDocumentReference(params);
|
|
2533
|
+
if (normalized.documentId) {
|
|
2534
|
+
return this.readRemoteDocumentChunks(normalized.documentId, params);
|
|
2535
|
+
}
|
|
2536
|
+
if (normalized.localDocumentId && looksLikeRemoteDocumentId(normalized.localDocumentId) && !await this.store.getDocument(normalized.localDocumentId)) {
|
|
2537
|
+
return this.readRemoteDocumentChunks(normalized.localDocumentId, params);
|
|
2538
|
+
}
|
|
1853
2539
|
const { document, result } = await this.loadReadableResult(params);
|
|
1854
2540
|
const limit = clampLimit(params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
|
|
1855
2541
|
const chunks = indexChunks(result).filter((chunk) => matchesReadScope(chunk, params));
|
|
@@ -1866,6 +2552,13 @@ var Knowledge = class _Knowledge {
|
|
|
1866
2552
|
if (!params.pattern) {
|
|
1867
2553
|
throw new ValidationError("pattern is required");
|
|
1868
2554
|
}
|
|
2555
|
+
const normalized = normalizeDocumentReference(params);
|
|
2556
|
+
if (normalized.documentId) {
|
|
2557
|
+
return this.grepRemoteDocumentChunks(normalized.documentId, params);
|
|
2558
|
+
}
|
|
2559
|
+
if (normalized.localDocumentId && looksLikeRemoteDocumentId(normalized.localDocumentId) && !await this.store.getDocument(normalized.localDocumentId)) {
|
|
2560
|
+
return this.grepRemoteDocumentChunks(normalized.localDocumentId, params);
|
|
2561
|
+
}
|
|
1869
2562
|
const { document, result } = await this.loadReadableResult(params);
|
|
1870
2563
|
const maxResults = clampLimit(params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
1871
2564
|
const contextChars = params.contextChars ?? DEFAULT_CONTEXT_CHARS;
|
|
@@ -1945,7 +2638,7 @@ var Knowledge = class _Knowledge {
|
|
|
1945
2638
|
});
|
|
1946
2639
|
}
|
|
1947
2640
|
if (normalized.jobId) {
|
|
1948
|
-
return this.
|
|
2641
|
+
return this.importJobResult({
|
|
1949
2642
|
jobId: normalized.jobId,
|
|
1950
2643
|
localDocumentId: normalized.localDocumentId
|
|
1951
2644
|
});
|
|
@@ -1965,24 +2658,670 @@ var Knowledge = class _Knowledge {
|
|
|
1965
2658
|
}
|
|
1966
2659
|
throw new Error(`Local Knowhere document not found: ${normalized.localDocumentId}`);
|
|
1967
2660
|
}
|
|
1968
|
-
async
|
|
1969
|
-
const
|
|
1970
|
-
|
|
2661
|
+
async readRemoteDocumentChunks(documentId, params) {
|
|
2662
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
2663
|
+
if (explicitRevisionKey) {
|
|
2664
|
+
const stored2 = await this.tryReadChunksFromParsedStorage({
|
|
2665
|
+
documentId,
|
|
2666
|
+
revisionKey: explicitRevisionKey,
|
|
2667
|
+
params
|
|
2668
|
+
});
|
|
2669
|
+
if (stored2) {
|
|
2670
|
+
return stored2;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
if (hasScanReadFilters(params)) {
|
|
2674
|
+
return this.scanRemoteDocumentChunks(documentId, params);
|
|
2675
|
+
}
|
|
2676
|
+
const pageParams = getReadPageParams(params);
|
|
2677
|
+
const remotePage = await this.client.documents.listChunks(documentId, {
|
|
2678
|
+
page: pageParams.page,
|
|
2679
|
+
pageSize: pageParams.pageSize,
|
|
2680
|
+
chunkType: params.chunkType,
|
|
2681
|
+
includeAssetUrls: shouldRequestRemoteAssetUrls(
|
|
2682
|
+
params.assetUrlPolicy,
|
|
2683
|
+
this.parsedStorageConfig
|
|
2684
|
+
)
|
|
2685
|
+
});
|
|
2686
|
+
const revisionKey = getRemoteRevisionKey(remotePage);
|
|
2687
|
+
const stored = await this.tryReadChunksFromParsedStorage({
|
|
2688
|
+
documentId,
|
|
2689
|
+
revisionKey,
|
|
2690
|
+
params
|
|
2691
|
+
});
|
|
2692
|
+
if (stored) {
|
|
2693
|
+
return stored;
|
|
2694
|
+
}
|
|
2695
|
+
const chunks = await this.toRemoteReadChunks({
|
|
2696
|
+
documentId,
|
|
2697
|
+
revisionKey,
|
|
2698
|
+
chunks: remotePage.chunks,
|
|
2699
|
+
assetUrlPolicy: params.assetUrlPolicy
|
|
2700
|
+
});
|
|
2701
|
+
this.scheduleParsedStorageSync(documentId, revisionKey);
|
|
2702
|
+
return {
|
|
2703
|
+
document: createRemoteKnowledgeDocument({
|
|
2704
|
+
documentId,
|
|
2705
|
+
response: remotePage,
|
|
2706
|
+
revisionKey
|
|
2707
|
+
}),
|
|
2708
|
+
chunks,
|
|
2709
|
+
page: remotePage.pagination.page,
|
|
2710
|
+
pageSize: remotePage.pagination.pageSize,
|
|
2711
|
+
totalChunks: remotePage.pagination.total,
|
|
2712
|
+
totalPages: remotePage.pagination.totalPages
|
|
2713
|
+
};
|
|
1971
2714
|
}
|
|
1972
|
-
async
|
|
1973
|
-
const
|
|
2715
|
+
async scanRemoteDocumentChunks(documentId, params) {
|
|
2716
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
2717
|
+
if (explicitRevisionKey) {
|
|
2718
|
+
const stored2 = await this.tryReadChunksFromParsedStorage({
|
|
2719
|
+
documentId,
|
|
2720
|
+
revisionKey: explicitRevisionKey,
|
|
2721
|
+
params
|
|
2722
|
+
});
|
|
2723
|
+
if (stored2) {
|
|
2724
|
+
return stored2;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
const limit = clampLimit(params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
|
|
2728
|
+
const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
|
|
2729
|
+
const firstPage = await this.client.documents.listChunks(documentId, {
|
|
1974
2730
|
page: 1,
|
|
1975
|
-
pageSize:
|
|
2731
|
+
pageSize: limits.remotePageSize,
|
|
2732
|
+
chunkType: params.chunkType,
|
|
2733
|
+
includeAssetUrls: shouldRequestRemoteAssetUrls(
|
|
2734
|
+
params.assetUrlPolicy,
|
|
2735
|
+
this.parsedStorageConfig
|
|
2736
|
+
)
|
|
1976
2737
|
});
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2738
|
+
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
2739
|
+
const stored = await this.tryReadChunksFromParsedStorage({
|
|
2740
|
+
documentId,
|
|
2741
|
+
revisionKey,
|
|
2742
|
+
params
|
|
2743
|
+
});
|
|
2744
|
+
if (stored) {
|
|
2745
|
+
return stored;
|
|
1981
2746
|
}
|
|
1982
|
-
|
|
2747
|
+
const selected = [];
|
|
2748
|
+
let scannedPages = 0;
|
|
2749
|
+
let page = 1;
|
|
2750
|
+
let response = firstPage;
|
|
2751
|
+
const startedAt = Date.now();
|
|
2752
|
+
while (true) {
|
|
2753
|
+
scannedPages += 1;
|
|
2754
|
+
selected.push(...response.chunks.filter((chunk) => matchesRemoteReadScope(chunk, params)));
|
|
2755
|
+
if (selected.length >= limit || response.pagination.page >= response.pagination.totalPages || scannedPages >= limits.outlineMaxPages || Date.now() - startedAt >= limits.outlineDeadlineMs) {
|
|
2756
|
+
break;
|
|
2757
|
+
}
|
|
2758
|
+
page += 1;
|
|
2759
|
+
response = await this.client.documents.listChunks(documentId, {
|
|
2760
|
+
page,
|
|
2761
|
+
pageSize: limits.remotePageSize,
|
|
2762
|
+
chunkType: params.chunkType,
|
|
2763
|
+
includeAssetUrls: shouldRequestRemoteAssetUrls(
|
|
2764
|
+
params.assetUrlPolicy,
|
|
2765
|
+
this.parsedStorageConfig
|
|
2766
|
+
)
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
const chunks = await this.toRemoteReadChunks({
|
|
2770
|
+
documentId,
|
|
2771
|
+
revisionKey,
|
|
2772
|
+
chunks: selected.slice(0, limit),
|
|
2773
|
+
assetUrlPolicy: params.assetUrlPolicy
|
|
2774
|
+
});
|
|
2775
|
+
this.scheduleParsedStorageSync(documentId, revisionKey);
|
|
2776
|
+
return {
|
|
2777
|
+
document: createRemoteKnowledgeDocument({
|
|
2778
|
+
documentId,
|
|
2779
|
+
response: firstPage,
|
|
2780
|
+
revisionKey
|
|
2781
|
+
}),
|
|
2782
|
+
chunks,
|
|
2783
|
+
nextChunk: selected.length > limit ? toChunkPosition(selected[limit]) : void 0,
|
|
2784
|
+
totalChunks: firstPage.pagination.total,
|
|
2785
|
+
totalPages: firstPage.pagination.totalPages
|
|
2786
|
+
};
|
|
1983
2787
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
2788
|
+
async grepRemoteDocumentChunks(documentId, params) {
|
|
2789
|
+
const explicitRevisionKey = params.revisionKey ?? params.jobId;
|
|
2790
|
+
if (explicitRevisionKey && !params.continuationCursor) {
|
|
2791
|
+
const stored2 = await this.tryGrepChunksFromParsedStorage({
|
|
2792
|
+
documentId,
|
|
2793
|
+
revisionKey: explicitRevisionKey,
|
|
2794
|
+
params
|
|
2795
|
+
});
|
|
2796
|
+
if (stored2) {
|
|
2797
|
+
return stored2;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
const maxResults = clampLimit(params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
2801
|
+
const contextChars = params.contextChars ?? DEFAULT_CONTEXT_CHARS;
|
|
2802
|
+
const matcher = createMatcher(params);
|
|
2803
|
+
const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
|
|
2804
|
+
const cursor = parseContinuationCursor(params.continuationCursor);
|
|
2805
|
+
const startPage = cursor?.documentId === documentId ? cursor.nextPage : 1;
|
|
2806
|
+
const firstPage = await this.client.documents.listChunks(documentId, {
|
|
2807
|
+
page: startPage,
|
|
2808
|
+
pageSize: limits.remotePageSize,
|
|
2809
|
+
chunkType: params.chunkType,
|
|
2810
|
+
includeAssetUrls: false
|
|
2811
|
+
});
|
|
2812
|
+
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
2813
|
+
const stored = startPage === 1 ? await this.tryGrepChunksFromParsedStorage({
|
|
2814
|
+
documentId,
|
|
2815
|
+
revisionKey,
|
|
2816
|
+
params
|
|
2817
|
+
}) : null;
|
|
2818
|
+
if (stored) {
|
|
2819
|
+
return stored;
|
|
2820
|
+
}
|
|
2821
|
+
const matches = [];
|
|
2822
|
+
let scannedChunks = 0;
|
|
2823
|
+
let scannedPages = 0;
|
|
2824
|
+
let response = firstPage;
|
|
2825
|
+
let page = startPage;
|
|
2826
|
+
const startedAt = Date.now();
|
|
2827
|
+
while (true) {
|
|
2828
|
+
scannedPages += 1;
|
|
2829
|
+
for (const [chunkIndex, chunk] of response.chunks.entries()) {
|
|
2830
|
+
if (response.pagination.page === startPage && cursor?.nextChunkIndex !== void 0 && chunkIndex < cursor.nextChunkIndex) {
|
|
2831
|
+
continue;
|
|
2832
|
+
}
|
|
2833
|
+
if (!matchesGrepScope(toIndexedRemoteChunk(chunk), params)) {
|
|
2834
|
+
continue;
|
|
2835
|
+
}
|
|
2836
|
+
scannedChunks += 1;
|
|
2837
|
+
const chunkMatches = matcher(chunk.content ?? "");
|
|
2838
|
+
for (const [matchIndex, match] of chunkMatches.entries()) {
|
|
2839
|
+
if (response.pagination.page === startPage && cursor?.nextChunkIndex === chunkIndex && cursor.nextMatchIndex !== void 0 && matchIndex < cursor.nextMatchIndex) {
|
|
2840
|
+
continue;
|
|
2841
|
+
}
|
|
2842
|
+
matches.push({
|
|
2843
|
+
position: toChunkPosition(chunk),
|
|
2844
|
+
chunkId: chunk.chunkId,
|
|
2845
|
+
chunkType: chunk.chunkType,
|
|
2846
|
+
sectionPath: chunk.sectionPath ?? "",
|
|
2847
|
+
sourceChunkPath: chunk.sourceChunkPath ?? chunk.sectionPath ?? "",
|
|
2848
|
+
filePath: chunk.filePath ?? void 0,
|
|
2849
|
+
startOffset: match.startOffset,
|
|
2850
|
+
endOffset: match.endOffset,
|
|
2851
|
+
snippet: buildSnippet(
|
|
2852
|
+
chunk.content ?? "",
|
|
2853
|
+
match.startOffset,
|
|
2854
|
+
match.endOffset,
|
|
2855
|
+
contextChars
|
|
2856
|
+
)
|
|
2857
|
+
});
|
|
2858
|
+
if (matches.length >= maxResults) {
|
|
2859
|
+
const continuationCursor = createGrepContinuationCursor({
|
|
2860
|
+
documentId,
|
|
2861
|
+
revisionKey,
|
|
2862
|
+
response,
|
|
2863
|
+
chunkIndex,
|
|
2864
|
+
matchIndex,
|
|
2865
|
+
chunkMatchCount: chunkMatches.length
|
|
2866
|
+
});
|
|
2867
|
+
this.scheduleParsedStorageSync(documentId, revisionKey);
|
|
2868
|
+
return {
|
|
2869
|
+
document: createRemoteKnowledgeDocument({
|
|
2870
|
+
documentId,
|
|
2871
|
+
response: firstPage,
|
|
2872
|
+
revisionKey
|
|
2873
|
+
}),
|
|
2874
|
+
matches,
|
|
2875
|
+
scannedChunks,
|
|
2876
|
+
truncated: continuationCursor !== void 0,
|
|
2877
|
+
continuationCursor
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
const shouldStop = response.pagination.page >= response.pagination.totalPages || scannedPages >= limits.grepMaxPages || Date.now() - startedAt >= limits.grepDeadlineMs;
|
|
2883
|
+
if (shouldStop) {
|
|
2884
|
+
const truncated = response.pagination.page < response.pagination.totalPages;
|
|
2885
|
+
this.scheduleParsedStorageSync(documentId, revisionKey);
|
|
2886
|
+
return {
|
|
2887
|
+
document: createRemoteKnowledgeDocument({
|
|
2888
|
+
documentId,
|
|
2889
|
+
response: firstPage,
|
|
2890
|
+
revisionKey
|
|
2891
|
+
}),
|
|
2892
|
+
matches,
|
|
2893
|
+
scannedChunks,
|
|
2894
|
+
truncated,
|
|
2895
|
+
continuationCursor: truncated ? createContinuationCursor({
|
|
2896
|
+
documentId,
|
|
2897
|
+
revisionKey,
|
|
2898
|
+
nextPage: response.pagination.page + 1
|
|
2899
|
+
}) : void 0
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
page += 1;
|
|
2903
|
+
response = await this.client.documents.listChunks(documentId, {
|
|
2904
|
+
page,
|
|
2905
|
+
pageSize: limits.remotePageSize,
|
|
2906
|
+
chunkType: params.chunkType,
|
|
2907
|
+
includeAssetUrls: false
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
async getRemoteDocumentOutline(documentId, revisionKeyOverride) {
|
|
2912
|
+
if (revisionKeyOverride) {
|
|
2913
|
+
const stored2 = await this.tryReadOutlineFromParsedStorage(documentId, revisionKeyOverride);
|
|
2914
|
+
if (stored2) {
|
|
2915
|
+
return stored2;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
|
|
2919
|
+
const firstPage = await this.client.documents.listChunks(documentId, {
|
|
2920
|
+
page: 1,
|
|
2921
|
+
pageSize: limits.remotePageSize,
|
|
2922
|
+
includeAssetUrls: false
|
|
2923
|
+
});
|
|
2924
|
+
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
2925
|
+
const stored = await this.tryReadOutlineFromParsedStorage(documentId, revisionKey);
|
|
2926
|
+
if (stored) {
|
|
2927
|
+
return stored;
|
|
2928
|
+
}
|
|
2929
|
+
const chunks = [];
|
|
2930
|
+
let page = 1;
|
|
2931
|
+
let response = firstPage;
|
|
2932
|
+
let scannedPages = 0;
|
|
2933
|
+
const startedAt = Date.now();
|
|
2934
|
+
while (true) {
|
|
2935
|
+
scannedPages += 1;
|
|
2936
|
+
chunks.push(...response.chunks.map(toIndexedRemoteChunk));
|
|
2937
|
+
if (response.pagination.page >= response.pagination.totalPages || scannedPages >= limits.outlineMaxPages || Date.now() - startedAt >= limits.outlineDeadlineMs) {
|
|
2938
|
+
break;
|
|
2939
|
+
}
|
|
2940
|
+
page += 1;
|
|
2941
|
+
response = await this.client.documents.listChunks(documentId, {
|
|
2942
|
+
page,
|
|
2943
|
+
pageSize: limits.remotePageSize,
|
|
2944
|
+
includeAssetUrls: false
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
const truncated = response.pagination.page < response.pagination.totalPages;
|
|
2948
|
+
const sections = buildFlatSectionsFromIndexedChunks(chunks);
|
|
2949
|
+
this.scheduleParsedStorageSync(documentId, revisionKey);
|
|
2950
|
+
return {
|
|
2951
|
+
document: createRemoteKnowledgeDocument({
|
|
2952
|
+
documentId,
|
|
2953
|
+
response: firstPage,
|
|
2954
|
+
revisionKey,
|
|
2955
|
+
typeCounts: countIndexedTypes(chunks)
|
|
2956
|
+
}),
|
|
2957
|
+
totalChunks: firstPage.pagination.total,
|
|
2958
|
+
typeCounts: countIndexedTypes(chunks),
|
|
2959
|
+
sections,
|
|
2960
|
+
sectionTree: nestSections(sections),
|
|
2961
|
+
truncated,
|
|
2962
|
+
continuationCursor: truncated ? createContinuationCursor({
|
|
2963
|
+
documentId,
|
|
2964
|
+
revisionKey,
|
|
2965
|
+
nextPage: response.pagination.page + 1
|
|
2966
|
+
}) : void 0
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
async tryReadChunksFromParsedStorage(params) {
|
|
2970
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
2971
|
+
if (!parsedStorageConfig) {
|
|
2972
|
+
return null;
|
|
2973
|
+
}
|
|
2974
|
+
const manifest = await this.readFreshParsedStorageManifest(
|
|
2975
|
+
params.documentId,
|
|
2976
|
+
params.revisionKey
|
|
2977
|
+
);
|
|
2978
|
+
if (!manifest) {
|
|
2979
|
+
return null;
|
|
2980
|
+
}
|
|
2981
|
+
const pageParams = getReadPageParams(params.params);
|
|
2982
|
+
const indexedChunks = await this.readStoredSnapshotChunks({
|
|
2983
|
+
documentId: params.documentId,
|
|
2984
|
+
revisionKey: params.revisionKey,
|
|
2985
|
+
manifest,
|
|
2986
|
+
chunkType: params.params.chunkType
|
|
2987
|
+
});
|
|
2988
|
+
if (!indexedChunks) {
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
const scopedChunks = indexedChunks.filter((chunk) => matchesReadScope(chunk, params.params));
|
|
2992
|
+
const selected = hasScanReadFilters(params.params) ? selectReadWindow(
|
|
2993
|
+
scopedChunks,
|
|
2994
|
+
params.params,
|
|
2995
|
+
clampLimit(params.params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT)
|
|
2996
|
+
) : scopedChunks.slice(
|
|
2997
|
+
(pageParams.page - 1) * pageParams.pageSize,
|
|
2998
|
+
pageParams.page * pageParams.pageSize
|
|
2999
|
+
);
|
|
3000
|
+
const visibleChunks = applyAssetUrlPolicyToReadChunks(
|
|
3001
|
+
selected.map(toReadChunk),
|
|
3002
|
+
params.params.assetUrlPolicy
|
|
3003
|
+
);
|
|
3004
|
+
const totalChunks = scopedChunks.length;
|
|
3005
|
+
return {
|
|
3006
|
+
document: createStoredKnowledgeDocument({
|
|
3007
|
+
documentId: params.documentId,
|
|
3008
|
+
manifest,
|
|
3009
|
+
revisionKey: params.revisionKey,
|
|
3010
|
+
typeCounts: countIndexedTypes(indexedChunks)
|
|
3011
|
+
}),
|
|
3012
|
+
chunks: visibleChunks,
|
|
3013
|
+
page: hasScanReadFilters(params.params) ? void 0 : pageParams.page,
|
|
3014
|
+
pageSize: hasScanReadFilters(params.params) ? void 0 : pageParams.pageSize,
|
|
3015
|
+
totalChunks,
|
|
3016
|
+
totalPages: Math.max(1, Math.ceil(totalChunks / pageParams.pageSize)),
|
|
3017
|
+
nextChunk: getNextChunkPosition(scopedChunks, selected)
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
async tryGrepChunksFromParsedStorage(params) {
|
|
3021
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3022
|
+
if (!parsedStorageConfig) {
|
|
3023
|
+
return null;
|
|
3024
|
+
}
|
|
3025
|
+
const manifest = await this.readFreshParsedStorageManifest(
|
|
3026
|
+
params.documentId,
|
|
3027
|
+
params.revisionKey
|
|
3028
|
+
);
|
|
3029
|
+
if (!manifest) {
|
|
3030
|
+
return null;
|
|
3031
|
+
}
|
|
3032
|
+
const indexedChunks = await this.readStoredSnapshotChunks({
|
|
3033
|
+
documentId: params.documentId,
|
|
3034
|
+
revisionKey: params.revisionKey,
|
|
3035
|
+
manifest,
|
|
3036
|
+
chunkType: params.params.chunkType
|
|
3037
|
+
});
|
|
3038
|
+
if (!indexedChunks) {
|
|
3039
|
+
return null;
|
|
3040
|
+
}
|
|
3041
|
+
const maxResults = clampLimit(params.params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
3042
|
+
const contextChars = params.params.contextChars ?? DEFAULT_CONTEXT_CHARS;
|
|
3043
|
+
const matcher = createMatcher(params.params);
|
|
3044
|
+
const scopedChunks = indexedChunks.filter((chunk) => matchesGrepScope(chunk, params.params));
|
|
3045
|
+
const matches = [];
|
|
3046
|
+
let scannedChunks = 0;
|
|
3047
|
+
for (const chunk of scopedChunks) {
|
|
3048
|
+
scannedChunks += 1;
|
|
3049
|
+
for (const match of matcher(chunk.content)) {
|
|
3050
|
+
matches.push({
|
|
3051
|
+
position: chunk.position,
|
|
3052
|
+
chunkId: chunk.chunkId,
|
|
3053
|
+
chunkType: chunk.chunkType,
|
|
3054
|
+
sectionPath: chunk.sectionPath,
|
|
3055
|
+
sourceChunkPath: chunk.sourceChunkPath,
|
|
3056
|
+
filePath: chunk.filePath,
|
|
3057
|
+
startOffset: match.startOffset,
|
|
3058
|
+
endOffset: match.endOffset,
|
|
3059
|
+
snippet: buildSnippet(chunk.content, match.startOffset, match.endOffset, contextChars)
|
|
3060
|
+
});
|
|
3061
|
+
if (matches.length >= maxResults) {
|
|
3062
|
+
return {
|
|
3063
|
+
document: createStoredKnowledgeDocument({
|
|
3064
|
+
documentId: params.documentId,
|
|
3065
|
+
manifest,
|
|
3066
|
+
revisionKey: params.revisionKey,
|
|
3067
|
+
typeCounts: countIndexedTypes(indexedChunks)
|
|
3068
|
+
}),
|
|
3069
|
+
matches,
|
|
3070
|
+
scannedChunks,
|
|
3071
|
+
truncated: true
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return {
|
|
3077
|
+
document: createStoredKnowledgeDocument({
|
|
3078
|
+
documentId: params.documentId,
|
|
3079
|
+
manifest,
|
|
3080
|
+
revisionKey: params.revisionKey,
|
|
3081
|
+
typeCounts: countIndexedTypes(indexedChunks)
|
|
3082
|
+
}),
|
|
3083
|
+
matches,
|
|
3084
|
+
scannedChunks,
|
|
3085
|
+
truncated: false
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
async tryReadOutlineFromParsedStorage(documentId, revisionKey) {
|
|
3089
|
+
const manifest = await this.readFreshParsedStorageManifest(documentId, revisionKey);
|
|
3090
|
+
if (!manifest) {
|
|
3091
|
+
return null;
|
|
3092
|
+
}
|
|
3093
|
+
const indexedChunks = await this.readStoredSnapshotChunks({
|
|
3094
|
+
documentId,
|
|
3095
|
+
revisionKey,
|
|
3096
|
+
manifest
|
|
3097
|
+
});
|
|
3098
|
+
if (!indexedChunks) {
|
|
3099
|
+
return null;
|
|
3100
|
+
}
|
|
3101
|
+
const sections = buildFlatSectionsFromIndexedChunks(indexedChunks);
|
|
3102
|
+
const typeCounts = countIndexedTypes(indexedChunks);
|
|
3103
|
+
return {
|
|
3104
|
+
document: createStoredKnowledgeDocument({
|
|
3105
|
+
documentId,
|
|
3106
|
+
manifest,
|
|
3107
|
+
revisionKey,
|
|
3108
|
+
typeCounts
|
|
3109
|
+
}),
|
|
3110
|
+
totalChunks: manifest.totalChunks,
|
|
3111
|
+
typeCounts,
|
|
3112
|
+
sections,
|
|
3113
|
+
sectionTree: nestSections(sections),
|
|
3114
|
+
truncated: false
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
async readFreshParsedStorageManifest(documentId, revisionKey) {
|
|
3118
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3119
|
+
if (!parsedStorageConfig) {
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
const manifest = await parsedStorageConfig.storage.readManifest({
|
|
3123
|
+
documentId,
|
|
3124
|
+
revisionKey
|
|
3125
|
+
});
|
|
3126
|
+
if (!manifest || !isFreshManifest(manifest, revisionKey)) {
|
|
3127
|
+
return null;
|
|
3128
|
+
}
|
|
3129
|
+
return manifest;
|
|
3130
|
+
}
|
|
3131
|
+
async readStoredSnapshotChunks(params) {
|
|
3132
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3133
|
+
if (!parsedStorageConfig) {
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
const chunks = [];
|
|
3137
|
+
for (const pageReference of params.manifest.chunkPages) {
|
|
3138
|
+
const page = await parsedStorageConfig.storage.readChunkPage({
|
|
3139
|
+
documentId: params.documentId,
|
|
3140
|
+
revisionKey: params.revisionKey,
|
|
3141
|
+
page: pageReference.page,
|
|
3142
|
+
chunkType: params.chunkType
|
|
3143
|
+
});
|
|
3144
|
+
if (!page) {
|
|
3145
|
+
return null;
|
|
3146
|
+
}
|
|
3147
|
+
chunks.push(
|
|
3148
|
+
...page.chunks.filter((chunk) => !params.chunkType || chunk.chunkType === params.chunkType).map(toIndexedSnapshotChunk)
|
|
3149
|
+
);
|
|
3150
|
+
}
|
|
3151
|
+
return chunks.sort((left, right) => left.position - right.position);
|
|
3152
|
+
}
|
|
3153
|
+
async toRemoteReadChunks(params) {
|
|
3154
|
+
const readChunks = params.chunks.map((chunk) => toRemoteReadChunk(chunk));
|
|
3155
|
+
if (params.assetUrlPolicy === "durable") {
|
|
3156
|
+
return this.hardenReadChunkAssetUrls({
|
|
3157
|
+
documentId: params.documentId,
|
|
3158
|
+
revisionKey: params.revisionKey,
|
|
3159
|
+
chunks: readChunks
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
return applyAssetUrlPolicyToReadChunks(readChunks, params.assetUrlPolicy);
|
|
3163
|
+
}
|
|
3164
|
+
async hardenReadChunkAssetUrls(params) {
|
|
3165
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3166
|
+
if (!parsedStorageConfig) {
|
|
3167
|
+
return applyAssetUrlPolicyToReadChunks(params.chunks, "none");
|
|
3168
|
+
}
|
|
3169
|
+
const hardenedChunks = [];
|
|
3170
|
+
for (const chunk of params.chunks) {
|
|
3171
|
+
hardenedChunks.push(
|
|
3172
|
+
await hardenReadChunkAssetUrls({
|
|
3173
|
+
storageConfig: parsedStorageConfig,
|
|
3174
|
+
documentId: params.documentId,
|
|
3175
|
+
revisionKey: params.revisionKey,
|
|
3176
|
+
chunk
|
|
3177
|
+
})
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
3180
|
+
return hardenedChunks;
|
|
3181
|
+
}
|
|
3182
|
+
scheduleParsedStorageSync(documentId, revisionKey) {
|
|
3183
|
+
const parsedStorageConfig = this.parsedStorageConfig;
|
|
3184
|
+
if (!parsedStorageConfig) {
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
const scheduler = parsedStorageConfig.scheduler ?? defaultParsedStorageScheduler;
|
|
3188
|
+
const task = async () => {
|
|
3189
|
+
try {
|
|
3190
|
+
await this.syncRemoteDocumentToParsedStorage({
|
|
3191
|
+
documentId,
|
|
3192
|
+
revisionKey,
|
|
3193
|
+
parsedStorageConfig
|
|
3194
|
+
});
|
|
3195
|
+
} catch (error) {
|
|
3196
|
+
await parsedStorageConfig.storage.writeSyncProgress({
|
|
3197
|
+
documentId,
|
|
3198
|
+
revisionKey,
|
|
3199
|
+
nextChunkPage: 1,
|
|
3200
|
+
nextAssetIndex: 0,
|
|
3201
|
+
status: "failed",
|
|
3202
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3203
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3204
|
+
});
|
|
3205
|
+
}
|
|
3206
|
+
};
|
|
3207
|
+
void Promise.resolve(scheduler.schedule(task)).catch(() => void 0);
|
|
3208
|
+
}
|
|
3209
|
+
async syncRemoteDocumentToParsedStorage(params) {
|
|
3210
|
+
const limits = getParsedStorageLimits(params.parsedStorageConfig.limits);
|
|
3211
|
+
const existingProgress = params.revisionKey ? await params.parsedStorageConfig.storage.readSyncProgress({
|
|
3212
|
+
documentId: params.documentId,
|
|
3213
|
+
revisionKey: params.revisionKey
|
|
3214
|
+
}) : null;
|
|
3215
|
+
let nextPage = existingProgress?.status === "running" ? existingProgress.nextChunkPage : 1;
|
|
3216
|
+
let response = await this.client.documents.listChunks(params.documentId, {
|
|
3217
|
+
page: nextPage,
|
|
3218
|
+
pageSize: limits.remotePageSize,
|
|
3219
|
+
includeAssetUrls: true
|
|
3220
|
+
});
|
|
3221
|
+
const revisionKey = params.revisionKey ?? getRemoteRevisionKey(response);
|
|
3222
|
+
if (params.revisionKey && getRemoteRevisionKey(response) !== params.revisionKey) {
|
|
3223
|
+
response = await this.client.documents.listChunks(params.documentId, {
|
|
3224
|
+
page: 1,
|
|
3225
|
+
pageSize: limits.remotePageSize,
|
|
3226
|
+
includeAssetUrls: true
|
|
3227
|
+
});
|
|
3228
|
+
nextPage = 1;
|
|
3229
|
+
}
|
|
3230
|
+
const totalPages = response.pagination.totalPages;
|
|
3231
|
+
const chunkPages = [];
|
|
3232
|
+
let syncedPages = 0;
|
|
3233
|
+
const startedAt = Date.now();
|
|
3234
|
+
while (nextPage <= totalPages) {
|
|
3235
|
+
if (syncedPages > 0) {
|
|
3236
|
+
response = await this.client.documents.listChunks(params.documentId, {
|
|
3237
|
+
page: nextPage,
|
|
3238
|
+
pageSize: limits.remotePageSize,
|
|
3239
|
+
includeAssetUrls: true
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
const readChunks = await this.hardenReadChunkAssetUrls({
|
|
3243
|
+
documentId: params.documentId,
|
|
3244
|
+
revisionKey,
|
|
3245
|
+
chunks: response.chunks.map(toRemoteReadChunk)
|
|
3246
|
+
});
|
|
3247
|
+
const page = toSnapshotChunkPageFromReadChunks({
|
|
3248
|
+
response,
|
|
3249
|
+
revisionKey,
|
|
3250
|
+
chunks: readChunks
|
|
3251
|
+
});
|
|
3252
|
+
await params.parsedStorageConfig.storage.writeChunkPage({
|
|
3253
|
+
documentId: params.documentId,
|
|
3254
|
+
revisionKey,
|
|
3255
|
+
page
|
|
3256
|
+
});
|
|
3257
|
+
chunkPages.push(page);
|
|
3258
|
+
nextPage += 1;
|
|
3259
|
+
syncedPages += 1;
|
|
3260
|
+
await params.parsedStorageConfig.storage.writeSyncProgress({
|
|
3261
|
+
documentId: params.documentId,
|
|
3262
|
+
revisionKey,
|
|
3263
|
+
nextChunkPage: nextPage,
|
|
3264
|
+
nextAssetIndex: 0,
|
|
3265
|
+
status: "running",
|
|
3266
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3267
|
+
});
|
|
3268
|
+
if (syncedPages >= limits.maxPagesPerSync || Date.now() - startedAt >= limits.syncDeadlineMs) {
|
|
3269
|
+
return {
|
|
3270
|
+
documentId: params.documentId,
|
|
3271
|
+
revisionKey,
|
|
3272
|
+
completed: false
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
const manifest = createRemoteSnapshotManifest({
|
|
3277
|
+
response,
|
|
3278
|
+
revisionKey,
|
|
3279
|
+
chunkPages
|
|
3280
|
+
});
|
|
3281
|
+
await params.parsedStorageConfig.storage.writeManifest({
|
|
3282
|
+
documentId: params.documentId,
|
|
3283
|
+
revisionKey,
|
|
3284
|
+
manifest
|
|
3285
|
+
});
|
|
3286
|
+
await params.parsedStorageConfig.storage.writeSyncProgress({
|
|
3287
|
+
documentId: params.documentId,
|
|
3288
|
+
revisionKey,
|
|
3289
|
+
nextChunkPage: totalPages + 1,
|
|
3290
|
+
nextAssetIndex: 0,
|
|
3291
|
+
status: "completed",
|
|
3292
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3293
|
+
});
|
|
3294
|
+
return {
|
|
3295
|
+
documentId: params.documentId,
|
|
3296
|
+
revisionKey,
|
|
3297
|
+
completed: true,
|
|
3298
|
+
manifest
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
requireParsedStorageConfig() {
|
|
3302
|
+
if (!this.parsedStorageConfig) {
|
|
3303
|
+
throw new ValidationError("parsed document storage is not configured");
|
|
3304
|
+
}
|
|
3305
|
+
return this.parsedStorageConfig;
|
|
3306
|
+
}
|
|
3307
|
+
async findLocalDocumentByRemoteId(documentId) {
|
|
3308
|
+
const documents = await this.store.listDocuments();
|
|
3309
|
+
return documents.find((document) => document.documentId === documentId);
|
|
3310
|
+
}
|
|
3311
|
+
async getPublishedDocumentJobId(documentId) {
|
|
3312
|
+
const response = await this.client.documents.listChunks(documentId, {
|
|
3313
|
+
page: 1,
|
|
3314
|
+
pageSize: 1
|
|
3315
|
+
});
|
|
3316
|
+
if (!response.jobId) {
|
|
3317
|
+
throw new Error(
|
|
3318
|
+
`Cannot sync server document ${documentId}: current published job id was not returned.`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
return response.jobId;
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
function normalizeDocumentReference(reference) {
|
|
1986
3325
|
if (typeof reference === "string") {
|
|
1987
3326
|
return { localDocumentId: reference };
|
|
1988
3327
|
}
|
|
@@ -1998,22 +3337,525 @@ function createLocalDocumentIdForRemote(documentId) {
|
|
|
1998
3337
|
const hash = createHash2("sha256").update(documentId).digest("hex").slice(0, 16);
|
|
1999
3338
|
return `remote_${hash}`;
|
|
2000
3339
|
}
|
|
3340
|
+
var defaultParsedStorageScheduler = {
|
|
3341
|
+
schedule(task) {
|
|
3342
|
+
void task();
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
function getParsedStorageLimits(limits) {
|
|
3346
|
+
return {
|
|
3347
|
+
chunkPageSize: clampLimit(limits?.chunkPageSize, DEFAULT_REMOTE_SCAN_PAGE_SIZE, MAX_PAGE_SIZE),
|
|
3348
|
+
remotePageSize: clampLimit(
|
|
3349
|
+
limits?.remotePageSize,
|
|
3350
|
+
DEFAULT_REMOTE_SCAN_PAGE_SIZE,
|
|
3351
|
+
MAX_PAGE_SIZE
|
|
3352
|
+
),
|
|
3353
|
+
maxPagesPerSync: clampLimit(limits?.maxPagesPerSync, DEFAULT_MAX_SYNC_PAGES, 1e3),
|
|
3354
|
+
maxAssetsPerSync: clampLimit(limits?.maxAssetsPerSync, DEFAULT_MAX_SYNC_ASSETS, 1e3),
|
|
3355
|
+
syncDeadlineMs: clampLimit(limits?.syncDeadlineMs, DEFAULT_SYNC_DEADLINE_MS, 6e4),
|
|
3356
|
+
grepMaxPages: clampLimit(limits?.grepMaxPages, DEFAULT_GREP_MAX_PAGES, 1e3),
|
|
3357
|
+
grepDeadlineMs: clampLimit(limits?.grepDeadlineMs, DEFAULT_GREP_DEADLINE_MS, 6e4),
|
|
3358
|
+
outlineMaxPages: clampLimit(limits?.outlineMaxPages, DEFAULT_OUTLINE_MAX_PAGES, 1e3),
|
|
3359
|
+
outlineDeadlineMs: clampLimit(limits?.outlineDeadlineMs, DEFAULT_OUTLINE_DEADLINE_MS, 6e4)
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
function validateReadParams(params) {
|
|
3363
|
+
const usesPagedDisplay = params.page !== void 0 || params.pageSize !== void 0 || params.assetUrlPolicy !== void 0;
|
|
3364
|
+
if (!usesPagedDisplay) {
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (params.sectionPath !== void 0 || params.startChunk !== void 0 || params.endChunk !== void 0 || params.chunkId !== void 0) {
|
|
3368
|
+
throw new ValidationError(
|
|
3369
|
+
"page, pageSize, and assetUrlPolicy cannot be combined with sectionPath, startChunk, endChunk, or chunkId"
|
|
3370
|
+
);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
function hasScanReadFilters(params) {
|
|
3374
|
+
return params.sectionPath !== void 0 || params.startChunk !== void 0 || params.endChunk !== void 0 || params.chunkId !== void 0;
|
|
3375
|
+
}
|
|
3376
|
+
function getReadPageParams(params) {
|
|
3377
|
+
return {
|
|
3378
|
+
page: clampLimit(params.page, 1, Number.MAX_SAFE_INTEGER),
|
|
3379
|
+
pageSize: clampLimit(params.pageSize ?? params.limit, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE)
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
function shouldRequestRemoteAssetUrls(assetUrlPolicy, parsedStorageConfig) {
|
|
3383
|
+
return assetUrlPolicy === "durable" && parsedStorageConfig !== void 0;
|
|
3384
|
+
}
|
|
3385
|
+
function getRemoteRevisionKey(response) {
|
|
3386
|
+
const revisionKey = response.jobResultId ?? response.jobId;
|
|
3387
|
+
if (!revisionKey) {
|
|
3388
|
+
throw new Error(
|
|
3389
|
+
`Cannot read server document ${response.documentId}: current published revision was not returned.`
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
3392
|
+
return revisionKey;
|
|
3393
|
+
}
|
|
3394
|
+
function createRemoteKnowledgeDocument(params) {
|
|
3395
|
+
return {
|
|
3396
|
+
localDocumentId: params.documentId,
|
|
3397
|
+
jobId: params.response.jobId ?? params.revisionKey,
|
|
3398
|
+
documentId: params.documentId,
|
|
3399
|
+
namespace: params.response.namespace,
|
|
3400
|
+
sourceFileName: params.documentId,
|
|
3401
|
+
chunkCount: params.response.pagination.total,
|
|
3402
|
+
typeCounts: params.typeCounts ?? countRemoteChunkTypes(params.response.chunks),
|
|
3403
|
+
resultDirectoryPath: `remote:${params.documentId}`,
|
|
3404
|
+
createdAt: /* @__PURE__ */ new Date(0),
|
|
3405
|
+
updatedAt: /* @__PURE__ */ new Date(0)
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
function createStoredKnowledgeDocument(params) {
|
|
3409
|
+
return {
|
|
3410
|
+
localDocumentId: params.documentId,
|
|
3411
|
+
jobId: params.manifest.jobId || params.revisionKey,
|
|
3412
|
+
documentId: params.manifest.documentId ?? params.documentId,
|
|
3413
|
+
namespace: params.manifest.namespace,
|
|
3414
|
+
sourceFileName: params.manifest.sourceFileName,
|
|
3415
|
+
chunkCount: params.manifest.totalChunks,
|
|
3416
|
+
typeCounts: params.typeCounts,
|
|
3417
|
+
resultDirectoryPath: `parsed-storage:${params.documentId}`,
|
|
3418
|
+
createdAt: new Date(params.manifest.createdAt),
|
|
3419
|
+
updatedAt: new Date(params.manifest.createdAt)
|
|
3420
|
+
};
|
|
3421
|
+
}
|
|
3422
|
+
function isFreshManifest(manifest, revisionKey) {
|
|
3423
|
+
return (manifest.revisionKey ?? manifest.jobId) === revisionKey;
|
|
3424
|
+
}
|
|
3425
|
+
function toRemoteReadChunk(chunk) {
|
|
3426
|
+
const metadata = cloneMetadata(chunk.metadata);
|
|
3427
|
+
return {
|
|
3428
|
+
position: toChunkPosition(chunk),
|
|
3429
|
+
chunkId: chunk.chunkId,
|
|
3430
|
+
chunkType: chunk.chunkType,
|
|
3431
|
+
contentSource: chunk.contentSource ?? void 0,
|
|
3432
|
+
content: chunk.content ?? "",
|
|
3433
|
+
readableContent: getReadableRemoteChunkContent(chunk, metadata),
|
|
3434
|
+
sectionPath: chunk.sectionPath ?? "",
|
|
3435
|
+
sourceChunkPath: chunk.sourceChunkPath ?? chunk.sectionPath ?? "",
|
|
3436
|
+
filePath: chunk.filePath ?? void 0,
|
|
3437
|
+
assetUrl: chunk.assetUrl ?? void 0,
|
|
3438
|
+
pageNumbers: getChunkPageNumbers(metadata),
|
|
3439
|
+
metadata
|
|
3440
|
+
};
|
|
3441
|
+
}
|
|
3442
|
+
function getReadableRemoteChunkContent(chunk, metadata) {
|
|
3443
|
+
if (chunk.chunkType !== "page") {
|
|
3444
|
+
return chunk.content ?? "";
|
|
3445
|
+
}
|
|
3446
|
+
const summary = metadata.summary;
|
|
3447
|
+
return typeof summary === "string" && summary.trim().length > 0 ? summary : chunk.content ?? "";
|
|
3448
|
+
}
|
|
3449
|
+
function toIndexedRemoteChunk(chunk) {
|
|
3450
|
+
const readChunk = toRemoteReadChunk(chunk);
|
|
3451
|
+
return toIndexedReadChunk(readChunk);
|
|
3452
|
+
}
|
|
3453
|
+
function toIndexedSnapshotChunk(chunk) {
|
|
3454
|
+
return toIndexedReadChunk({
|
|
3455
|
+
position: chunk.sortOrder,
|
|
3456
|
+
chunkId: chunk.chunkId,
|
|
3457
|
+
chunkType: chunk.chunkType,
|
|
3458
|
+
contentSource: chunk.contentSource,
|
|
3459
|
+
content: chunk.content,
|
|
3460
|
+
readableContent: chunk.chunkType === "page" && typeof chunk.metadata.summary === "string" && chunk.metadata.summary.trim().length > 0 ? chunk.metadata.summary : chunk.content,
|
|
3461
|
+
sectionPath: chunk.sectionPath ?? "",
|
|
3462
|
+
sourceChunkPath: chunk.sourceChunkPath,
|
|
3463
|
+
filePath: chunk.filePath,
|
|
3464
|
+
assetUrl: chunk.assetUrl,
|
|
3465
|
+
pageNumbers: getChunkPageNumbers(chunk.metadata),
|
|
3466
|
+
metadata: cloneMetadata(chunk.metadata)
|
|
3467
|
+
});
|
|
3468
|
+
}
|
|
3469
|
+
function toIndexedReadChunk(chunk) {
|
|
3470
|
+
return {
|
|
3471
|
+
source: createSyntheticChunk(chunk),
|
|
3472
|
+
position: chunk.position,
|
|
3473
|
+
chunkId: chunk.chunkId,
|
|
3474
|
+
chunkType: chunk.chunkType,
|
|
3475
|
+
contentSource: chunk.contentSource,
|
|
3476
|
+
content: chunk.content,
|
|
3477
|
+
readableContent: chunk.readableContent,
|
|
3478
|
+
sectionPath: chunk.sectionPath,
|
|
3479
|
+
sourceChunkPath: chunk.sourceChunkPath,
|
|
3480
|
+
filePath: chunk.filePath,
|
|
3481
|
+
assetUrl: chunk.assetUrl,
|
|
3482
|
+
pageNumbers: chunk.pageNumbers,
|
|
3483
|
+
metadata: chunk.metadata
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
function createSyntheticChunk(chunk) {
|
|
3487
|
+
return {
|
|
3488
|
+
chunkId: chunk.chunkId,
|
|
3489
|
+
type: chunk.chunkType,
|
|
3490
|
+
contentSource: chunk.contentSource,
|
|
3491
|
+
content: chunk.content,
|
|
3492
|
+
path: chunk.sourceChunkPath,
|
|
3493
|
+
metadata: chunk.metadata
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
function toChunkPosition(chunk) {
|
|
3497
|
+
return chunk ? chunk.sortOrder + 1 : 1;
|
|
3498
|
+
}
|
|
3499
|
+
function buildFlatSectionsFromIndexedChunks(chunks) {
|
|
3500
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
3501
|
+
for (const chunk of chunks) {
|
|
3502
|
+
const path4 = chunk.sectionPath || chunk.sourceChunkPath;
|
|
3503
|
+
const existing = byPath.get(path4);
|
|
3504
|
+
if (existing) {
|
|
3505
|
+
addChunkToSection(existing, chunk);
|
|
3506
|
+
} else {
|
|
3507
|
+
byPath.set(path4, createSectionFromChunk(path4, chunk));
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
return [...byPath.values()].sort(compareSections);
|
|
3511
|
+
}
|
|
3512
|
+
function matchesRemoteReadScope(chunk, params) {
|
|
3513
|
+
return matchesReadScope(toIndexedRemoteChunk(chunk), params);
|
|
3514
|
+
}
|
|
3515
|
+
function getNextChunkPosition(chunks, selected) {
|
|
3516
|
+
const lastSelected = selected[selected.length - 1];
|
|
3517
|
+
if (!lastSelected || selected.length >= chunks.length) {
|
|
3518
|
+
return void 0;
|
|
3519
|
+
}
|
|
3520
|
+
return chunks[chunks.indexOf(lastSelected) + 1]?.position;
|
|
3521
|
+
}
|
|
3522
|
+
function applyAssetUrlPolicyToReadChunks(chunks, assetUrlPolicy) {
|
|
3523
|
+
if (assetUrlPolicy !== "none") {
|
|
3524
|
+
return chunks.map((chunk) => ({
|
|
3525
|
+
...chunk,
|
|
3526
|
+
metadata: cloneMetadata(chunk.metadata)
|
|
3527
|
+
}));
|
|
3528
|
+
}
|
|
3529
|
+
return chunks.map((chunk) => ({
|
|
3530
|
+
...chunk,
|
|
3531
|
+
assetUrl: void 0,
|
|
3532
|
+
metadata: removeAssetUrlsFromMetadata(chunk.metadata)
|
|
3533
|
+
}));
|
|
3534
|
+
}
|
|
3535
|
+
async function hardenReadChunkAssetUrls(params) {
|
|
3536
|
+
const metadata = cloneMetadata(params.chunk.metadata);
|
|
3537
|
+
const assetUrl = await hardenPrimaryAssetUrl(params, metadata);
|
|
3538
|
+
const pageAssets = await hardenPageAssetUrls(params, metadata);
|
|
3539
|
+
if (pageAssets) {
|
|
3540
|
+
metadata.pageAssets = pageAssets;
|
|
3541
|
+
}
|
|
3542
|
+
return {
|
|
3543
|
+
...params.chunk,
|
|
3544
|
+
assetUrl,
|
|
3545
|
+
metadata
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
3548
|
+
async function hardenPrimaryAssetUrl(params, metadata) {
|
|
3549
|
+
if (!params.chunk.assetUrl || !params.chunk.filePath) {
|
|
3550
|
+
return void 0;
|
|
3551
|
+
}
|
|
3552
|
+
return writeDurableAssetUrl({
|
|
3553
|
+
storageConfig: params.storageConfig,
|
|
3554
|
+
documentId: params.documentId,
|
|
3555
|
+
revisionKey: params.revisionKey,
|
|
3556
|
+
sourcePath: params.chunk.filePath,
|
|
3557
|
+
sourceUrl: params.chunk.assetUrl,
|
|
3558
|
+
fallbackContentType: inferContentTypeFromPath(params.chunk.filePath),
|
|
3559
|
+
metadata: {
|
|
3560
|
+
chunkId: params.chunk.chunkId,
|
|
3561
|
+
chunkType: params.chunk.chunkType,
|
|
3562
|
+
sourcePath: params.chunk.filePath,
|
|
3563
|
+
...stringifyFlatMetadata(metadata)
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
async function hardenPageAssetUrls(params, metadata) {
|
|
3568
|
+
const pageAssets = toUnknownArray(metadata.pageAssets);
|
|
3569
|
+
if (!pageAssets) {
|
|
3570
|
+
return void 0;
|
|
3571
|
+
}
|
|
3572
|
+
const hardenedAssets = [];
|
|
3573
|
+
for (const pageAsset of pageAssets) {
|
|
3574
|
+
if (!isRecord2(pageAsset)) {
|
|
3575
|
+
hardenedAssets.push(pageAsset);
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
const artifactRef = pageAsset.artifactRef;
|
|
3579
|
+
const sourceUrl = pageAsset.assetUrl;
|
|
3580
|
+
if (typeof artifactRef !== "string" || typeof sourceUrl !== "string") {
|
|
3581
|
+
hardenedAssets.push(removeAssetUrlField(pageAsset));
|
|
3582
|
+
continue;
|
|
3583
|
+
}
|
|
3584
|
+
const durableUrl = await writeDurableAssetUrl({
|
|
3585
|
+
storageConfig: params.storageConfig,
|
|
3586
|
+
documentId: params.documentId,
|
|
3587
|
+
revisionKey: params.revisionKey,
|
|
3588
|
+
sourcePath: artifactRef,
|
|
3589
|
+
sourceUrl,
|
|
3590
|
+
fallbackContentType: typeof pageAsset.contentType === "string" ? pageAsset.contentType : inferContentTypeFromPath(artifactRef),
|
|
3591
|
+
metadata: {
|
|
3592
|
+
chunkId: params.chunk.chunkId,
|
|
3593
|
+
chunkType: params.chunk.chunkType,
|
|
3594
|
+
sourcePath: artifactRef
|
|
3595
|
+
}
|
|
3596
|
+
});
|
|
3597
|
+
const rest = removeAssetUrlField(pageAsset);
|
|
3598
|
+
hardenedAssets.push(durableUrl ? { ...rest, assetUrl: durableUrl } : rest);
|
|
3599
|
+
}
|
|
3600
|
+
return hardenedAssets;
|
|
3601
|
+
}
|
|
3602
|
+
async function writeDurableAssetUrl(params) {
|
|
3603
|
+
const existingUrl = await params.storageConfig.storage.getAssetUrl({
|
|
3604
|
+
documentId: params.documentId,
|
|
3605
|
+
revisionKey: params.revisionKey,
|
|
3606
|
+
sourcePath: params.sourcePath
|
|
3607
|
+
});
|
|
3608
|
+
if (existingUrl) {
|
|
3609
|
+
return existingUrl;
|
|
3610
|
+
}
|
|
3611
|
+
try {
|
|
3612
|
+
const fetched = await fetchAsset(params.sourceUrl, params.fallbackContentType);
|
|
3613
|
+
const written = await params.storageConfig.storage.writeAsset({
|
|
3614
|
+
documentId: params.documentId,
|
|
3615
|
+
revisionKey: params.revisionKey,
|
|
3616
|
+
sourcePath: params.sourcePath,
|
|
3617
|
+
body: fetched.body,
|
|
3618
|
+
contentType: fetched.contentType,
|
|
3619
|
+
metadata: params.metadata
|
|
3620
|
+
});
|
|
3621
|
+
return written.url ?? await params.storageConfig.storage.getAssetUrl({
|
|
3622
|
+
documentId: params.documentId,
|
|
3623
|
+
revisionKey: params.revisionKey,
|
|
3624
|
+
sourcePath: params.sourcePath
|
|
3625
|
+
}) ?? void 0;
|
|
3626
|
+
} catch {
|
|
3627
|
+
return void 0;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
async function fetchAsset(sourceUrl, fallbackContentType) {
|
|
3631
|
+
const response = await fetch(sourceUrl);
|
|
3632
|
+
if (!response.ok) {
|
|
3633
|
+
throw new Error(`Unable to fetch parsed asset: ${response.status}`);
|
|
3634
|
+
}
|
|
3635
|
+
return {
|
|
3636
|
+
body: new Uint8Array(await response.arrayBuffer()),
|
|
3637
|
+
contentType: response.headers.get("content-type") ?? fallbackContentType
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
function removeAssetUrlsFromMetadata(metadata) {
|
|
3641
|
+
const cloned = cloneMetadata(metadata);
|
|
3642
|
+
const pageAssets = toUnknownArray(cloned.pageAssets);
|
|
3643
|
+
if (!pageAssets) {
|
|
3644
|
+
return cloned;
|
|
3645
|
+
}
|
|
3646
|
+
cloned.pageAssets = pageAssets.map((pageAsset) => {
|
|
3647
|
+
if (!isRecord2(pageAsset)) {
|
|
3648
|
+
return pageAsset;
|
|
3649
|
+
}
|
|
3650
|
+
return removeAssetUrlField(pageAsset);
|
|
3651
|
+
});
|
|
3652
|
+
return cloned;
|
|
3653
|
+
}
|
|
3654
|
+
function removeAssetUrlField(value) {
|
|
3655
|
+
const cloned = { ...value };
|
|
3656
|
+
delete cloned.assetUrl;
|
|
3657
|
+
return cloned;
|
|
3658
|
+
}
|
|
3659
|
+
function toSnapshotChunkPageFromReadChunks(params) {
|
|
3660
|
+
return {
|
|
3661
|
+
version: 1,
|
|
3662
|
+
jobId: params.response.jobId ?? params.revisionKey,
|
|
3663
|
+
revisionKey: params.revisionKey,
|
|
3664
|
+
documentId: params.response.documentId,
|
|
3665
|
+
namespace: params.response.namespace,
|
|
3666
|
+
sourceFileName: params.response.documentId,
|
|
3667
|
+
page: params.response.pagination.page,
|
|
3668
|
+
pageSize: params.response.pagination.pageSize,
|
|
3669
|
+
total: params.response.pagination.total,
|
|
3670
|
+
totalPages: params.response.pagination.totalPages,
|
|
3671
|
+
chunks: params.chunks.map(toSnapshotChunkFromReadChunk)
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
function toSnapshotChunkFromReadChunk(chunk) {
|
|
3675
|
+
return {
|
|
3676
|
+
id: chunk.chunkId,
|
|
3677
|
+
chunkId: chunk.chunkId,
|
|
3678
|
+
chunkType: chunk.chunkType,
|
|
3679
|
+
contentSource: chunk.contentSource,
|
|
3680
|
+
content: chunk.content,
|
|
3681
|
+
sectionPath: chunk.sectionPath,
|
|
3682
|
+
sourceChunkPath: chunk.sourceChunkPath,
|
|
3683
|
+
filePath: chunk.filePath,
|
|
3684
|
+
sortOrder: chunk.position,
|
|
3685
|
+
metadata: cloneMetadata(chunk.metadata),
|
|
3686
|
+
assetUrl: chunk.assetUrl
|
|
3687
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
function createRemoteSnapshotManifest(params) {
|
|
3690
|
+
return {
|
|
3691
|
+
version: 1,
|
|
3692
|
+
kind: "knowhere-parsed-result-snapshot",
|
|
3693
|
+
jobId: params.response.jobId ?? params.revisionKey,
|
|
3694
|
+
revisionKey: params.revisionKey,
|
|
3695
|
+
documentId: params.response.documentId,
|
|
3696
|
+
namespace: params.response.namespace,
|
|
3697
|
+
sourceFileName: params.response.documentId,
|
|
3698
|
+
totalChunks: params.response.pagination.total,
|
|
3699
|
+
typeCounts: countSnapshotPagesTypes(params.chunkPages),
|
|
3700
|
+
chunkPageSize: params.response.pagination.pageSize,
|
|
3701
|
+
chunkPages: Array.from({ length: params.response.pagination.totalPages }, (_, index) => ({
|
|
3702
|
+
page: index + 1,
|
|
3703
|
+
pageSize: params.response.pagination.pageSize,
|
|
3704
|
+
chunkCount: index + 1 === params.response.pagination.totalPages ? params.response.pagination.total - params.response.pagination.pageSize * (params.response.pagination.totalPages - 1) : params.response.pagination.pageSize,
|
|
3705
|
+
key: `parsed/chunks/page-${index + 1}.json`
|
|
3706
|
+
})),
|
|
3707
|
+
assetUrlsByFilePath: {},
|
|
3708
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
function countSnapshotPagesTypes(pages) {
|
|
3712
|
+
return pages.flatMap((page) => page.chunks).reduce(
|
|
3713
|
+
(counts, chunk) => {
|
|
3714
|
+
const chunkType = chunk.chunkType;
|
|
3715
|
+
counts[chunkType] += 1;
|
|
3716
|
+
return counts;
|
|
3717
|
+
},
|
|
3718
|
+
{ text: 0, image: 0, table: 0, page: 0 }
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3721
|
+
function countRemoteChunkTypes(chunks) {
|
|
3722
|
+
return chunks.reduce(
|
|
3723
|
+
(counts, chunk) => {
|
|
3724
|
+
counts[chunk.chunkType] += 1;
|
|
3725
|
+
return counts;
|
|
3726
|
+
},
|
|
3727
|
+
{ text: 0, image: 0, table: 0, page: 0 }
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
function createContinuationCursor(cursor) {
|
|
3731
|
+
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
|
|
3732
|
+
}
|
|
3733
|
+
function createGrepContinuationCursor(params) {
|
|
3734
|
+
if (params.matchIndex + 1 < params.chunkMatchCount) {
|
|
3735
|
+
return createContinuationCursor({
|
|
3736
|
+
documentId: params.documentId,
|
|
3737
|
+
revisionKey: params.revisionKey,
|
|
3738
|
+
nextPage: params.response.pagination.page,
|
|
3739
|
+
nextChunkIndex: params.chunkIndex,
|
|
3740
|
+
nextMatchIndex: params.matchIndex + 1
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3743
|
+
if (params.chunkIndex + 1 < params.response.chunks.length) {
|
|
3744
|
+
return createContinuationCursor({
|
|
3745
|
+
documentId: params.documentId,
|
|
3746
|
+
revisionKey: params.revisionKey,
|
|
3747
|
+
nextPage: params.response.pagination.page,
|
|
3748
|
+
nextChunkIndex: params.chunkIndex + 1,
|
|
3749
|
+
nextMatchIndex: 0
|
|
3750
|
+
});
|
|
3751
|
+
}
|
|
3752
|
+
if (params.response.pagination.page < params.response.pagination.totalPages) {
|
|
3753
|
+
return createContinuationCursor({
|
|
3754
|
+
documentId: params.documentId,
|
|
3755
|
+
revisionKey: params.revisionKey,
|
|
3756
|
+
nextPage: params.response.pagination.page + 1,
|
|
3757
|
+
nextChunkIndex: 0,
|
|
3758
|
+
nextMatchIndex: 0
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
return void 0;
|
|
3762
|
+
}
|
|
3763
|
+
function parseContinuationCursor(value) {
|
|
3764
|
+
if (!value) {
|
|
3765
|
+
return null;
|
|
3766
|
+
}
|
|
3767
|
+
try {
|
|
3768
|
+
const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
3769
|
+
if (!isRecord2(parsed)) {
|
|
3770
|
+
return null;
|
|
3771
|
+
}
|
|
3772
|
+
const documentId = parsed.documentId;
|
|
3773
|
+
const revisionKey = parsed.revisionKey;
|
|
3774
|
+
const nextPage = parsed.nextPage;
|
|
3775
|
+
const nextChunkIndex = parsed.nextChunkIndex;
|
|
3776
|
+
const nextMatchIndex = parsed.nextMatchIndex;
|
|
3777
|
+
if (typeof documentId !== "string" || typeof revisionKey !== "string" || typeof nextPage !== "number" || !Number.isInteger(nextPage) || nextPage < 1 || !isOptionalNonNegativeInteger(nextChunkIndex) || !isOptionalNonNegativeInteger(nextMatchIndex)) {
|
|
3778
|
+
return null;
|
|
3779
|
+
}
|
|
3780
|
+
return {
|
|
3781
|
+
documentId,
|
|
3782
|
+
revisionKey,
|
|
3783
|
+
nextPage,
|
|
3784
|
+
nextChunkIndex,
|
|
3785
|
+
nextMatchIndex
|
|
3786
|
+
};
|
|
3787
|
+
} catch {
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
function isOptionalNonNegativeInteger(value) {
|
|
3792
|
+
return value === void 0 || typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
3793
|
+
}
|
|
3794
|
+
function cloneMetadata(metadata) {
|
|
3795
|
+
return { ...metadata };
|
|
3796
|
+
}
|
|
3797
|
+
function stringifyFlatMetadata(metadata) {
|
|
3798
|
+
const entries = Object.entries(metadata).flatMap(([key, value]) => {
|
|
3799
|
+
if (typeof value === "string") {
|
|
3800
|
+
return [[key, value]];
|
|
3801
|
+
}
|
|
3802
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
3803
|
+
return [[key, String(value)]];
|
|
3804
|
+
}
|
|
3805
|
+
return [];
|
|
3806
|
+
});
|
|
3807
|
+
return Object.fromEntries(entries);
|
|
3808
|
+
}
|
|
3809
|
+
function toUnknownArray(value) {
|
|
3810
|
+
return Array.isArray(value) ? value : null;
|
|
3811
|
+
}
|
|
3812
|
+
function inferContentTypeFromPath(filePath) {
|
|
3813
|
+
const extension = filePath.split(".").at(-1)?.toLowerCase();
|
|
3814
|
+
if (extension === "png") return "image/png";
|
|
3815
|
+
if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
|
|
3816
|
+
if (extension === "html" || extension === "htm") return "text/html; charset=utf-8";
|
|
3817
|
+
return "application/octet-stream";
|
|
3818
|
+
}
|
|
3819
|
+
function isRecord2(value) {
|
|
3820
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3821
|
+
}
|
|
2001
3822
|
function indexChunks(result) {
|
|
2002
3823
|
return result.chunks.map((chunk, index) => {
|
|
2003
3824
|
const filePath = getChunkFilePath2(chunk);
|
|
3825
|
+
const readableContent = getReadableChunkContent(chunk);
|
|
2004
3826
|
return {
|
|
2005
3827
|
source: chunk,
|
|
2006
3828
|
position: index + 1,
|
|
2007
3829
|
chunkId: chunk.chunkId,
|
|
2008
3830
|
chunkType: chunk.type,
|
|
3831
|
+
contentSource: chunk.contentSource,
|
|
2009
3832
|
content: chunk.content,
|
|
3833
|
+
readableContent,
|
|
2010
3834
|
sectionPath: normalizeSectionPath(chunk.path, result.manifest.sourceFileName),
|
|
2011
3835
|
sourceChunkPath: chunk.path,
|
|
2012
3836
|
filePath,
|
|
3837
|
+
assetUrl: getChunkAssetUrl(chunk),
|
|
3838
|
+
pageNumbers: getChunkPageNumbers(chunk.metadata),
|
|
2013
3839
|
metadata: chunk.metadata
|
|
2014
3840
|
};
|
|
2015
3841
|
});
|
|
2016
3842
|
}
|
|
3843
|
+
function getReadableChunkContent(chunk) {
|
|
3844
|
+
if (chunk.type !== "page") {
|
|
3845
|
+
return chunk.content;
|
|
3846
|
+
}
|
|
3847
|
+
const summary = chunk.metadata.summary;
|
|
3848
|
+
return typeof summary === "string" && summary.trim().length > 0 ? summary : chunk.content;
|
|
3849
|
+
}
|
|
3850
|
+
function getChunkPageNumbers(metadata) {
|
|
3851
|
+
const values = [metadata.pageNums, metadata.page_nums].filter(Array.isArray);
|
|
3852
|
+
const pageNumbers = values.flatMap(
|
|
3853
|
+
(value) => value.filter(
|
|
3854
|
+
(pageNumber) => Number.isInteger(pageNumber) && pageNumber > 0
|
|
3855
|
+
)
|
|
3856
|
+
);
|
|
3857
|
+
return pageNumbers.length > 0 ? [...new Set(pageNumbers)].sort((left, right) => left - right) : void 0;
|
|
3858
|
+
}
|
|
2017
3859
|
function getChunkFilePath2(chunk) {
|
|
2018
3860
|
if (chunk.type === "image" || chunk.type === "table") {
|
|
2019
3861
|
return chunk.filePath;
|
|
@@ -2021,14 +3863,17 @@ function getChunkFilePath2(chunk) {
|
|
|
2021
3863
|
const filePath = chunk.metadata.filePath;
|
|
2022
3864
|
return typeof filePath === "string" ? filePath : void 0;
|
|
2023
3865
|
}
|
|
2024
|
-
function
|
|
2025
|
-
|
|
3866
|
+
function getChunkAssetUrl(chunk) {
|
|
3867
|
+
return chunk.type === "image" || chunk.type === "table" ? chunk.assetUrl : void 0;
|
|
3868
|
+
}
|
|
3869
|
+
function normalizeSectionPath(path4, sourceFileName) {
|
|
3870
|
+
if (!path4) {
|
|
2026
3871
|
return "";
|
|
2027
3872
|
}
|
|
2028
|
-
if (
|
|
2029
|
-
return
|
|
3873
|
+
if (path4.startsWith("images/") || path4.startsWith("tables/")) {
|
|
3874
|
+
return path4;
|
|
2030
3875
|
}
|
|
2031
|
-
const parts =
|
|
3876
|
+
const parts = path4.split("/").filter(Boolean);
|
|
2032
3877
|
if (sourceFileName) {
|
|
2033
3878
|
const fileNameIndex = parts.indexOf(sourceFileName);
|
|
2034
3879
|
if (fileNameIndex >= 0) {
|
|
@@ -2050,12 +3895,12 @@ function buildFlatSections(result, chunks) {
|
|
|
2050
3895
|
}
|
|
2051
3896
|
const byPath = /* @__PURE__ */ new Map();
|
|
2052
3897
|
for (const chunk of chunks) {
|
|
2053
|
-
const
|
|
2054
|
-
const existing = byPath.get(
|
|
3898
|
+
const path4 = chunk.sectionPath || chunk.sourceChunkPath;
|
|
3899
|
+
const existing = byPath.get(path4);
|
|
2055
3900
|
if (existing) {
|
|
2056
3901
|
addChunkToSection(existing, chunk);
|
|
2057
3902
|
} else {
|
|
2058
|
-
byPath.set(
|
|
3903
|
+
byPath.set(path4, createSectionFromChunk(path4, chunk));
|
|
2059
3904
|
}
|
|
2060
3905
|
}
|
|
2061
3906
|
return [...byPath.values()].sort(compareSections);
|
|
@@ -2087,7 +3932,7 @@ function createSectionFromChunk(pathValue, chunk) {
|
|
|
2087
3932
|
startChunk: chunk.position,
|
|
2088
3933
|
endChunk: chunk.position,
|
|
2089
3934
|
chunkCount: 1,
|
|
2090
|
-
typeCounts: { text: 0, image: 0, table: 0, [chunk.chunkType]: 1 },
|
|
3935
|
+
typeCounts: { text: 0, image: 0, table: 0, page: 0, [chunk.chunkType]: 1 },
|
|
2091
3936
|
children: []
|
|
2092
3937
|
};
|
|
2093
3938
|
}
|
|
@@ -2146,7 +3991,7 @@ function countIndexedTypes(chunks) {
|
|
|
2146
3991
|
counts[chunk.chunkType] += 1;
|
|
2147
3992
|
return counts;
|
|
2148
3993
|
},
|
|
2149
|
-
{ text: 0, image: 0, table: 0 }
|
|
3994
|
+
{ text: 0, image: 0, table: 0, page: 0 }
|
|
2150
3995
|
);
|
|
2151
3996
|
}
|
|
2152
3997
|
function isInSection(chunkSectionPath, sectionPath) {
|
|
@@ -2183,10 +4028,14 @@ function toReadChunk(chunk) {
|
|
|
2183
4028
|
position: chunk.position,
|
|
2184
4029
|
chunkId: chunk.chunkId,
|
|
2185
4030
|
chunkType: chunk.chunkType,
|
|
4031
|
+
contentSource: chunk.contentSource,
|
|
2186
4032
|
content: chunk.content,
|
|
4033
|
+
readableContent: chunk.readableContent,
|
|
2187
4034
|
sectionPath: chunk.sectionPath,
|
|
2188
4035
|
sourceChunkPath: chunk.sourceChunkPath,
|
|
2189
4036
|
filePath: chunk.filePath,
|
|
4037
|
+
assetUrl: chunk.assetUrl,
|
|
4038
|
+
pageNumbers: chunk.pageNumbers,
|
|
2190
4039
|
metadata: chunk.metadata
|
|
2191
4040
|
};
|
|
2192
4041
|
}
|
|
@@ -2238,6 +4087,7 @@ function toResultReference(result, documentByServerId) {
|
|
|
2238
4087
|
return {
|
|
2239
4088
|
localDocumentId: documentId ? documentByServerId.get(documentId)?.localDocumentId : void 0,
|
|
2240
4089
|
documentId,
|
|
4090
|
+
chunkId: result.chunkId,
|
|
2241
4091
|
sectionPath: result.source.sectionPath ?? void 0,
|
|
2242
4092
|
chunkType: result.chunkType,
|
|
2243
4093
|
score: result.score
|
|
@@ -2248,6 +4098,7 @@ function toRemoteSearchResult(result, documentByServerId) {
|
|
|
2248
4098
|
return {
|
|
2249
4099
|
localDocumentId: documentId ? documentByServerId.get(documentId)?.localDocumentId : void 0,
|
|
2250
4100
|
documentId,
|
|
4101
|
+
chunkId: result.chunkId,
|
|
2251
4102
|
chunkType: result.chunkType,
|
|
2252
4103
|
content: result.content,
|
|
2253
4104
|
score: result.score,
|
|
@@ -2360,7 +4211,12 @@ var Knowhere = class {
|
|
|
2360
4211
|
const result = await this.jobs.load(jobResult, {
|
|
2361
4212
|
verifyChecksum: params.verifyChecksum
|
|
2362
4213
|
});
|
|
2363
|
-
|
|
4214
|
+
const enrichedResult = enrichParseResult(result, {
|
|
4215
|
+
namespace: jobResult.namespace,
|
|
4216
|
+
documentId: jobResult.documentId ?? params.documentId
|
|
4217
|
+
});
|
|
4218
|
+
const storedResult = await storeParseResultAssets(enrichedResult, params.storageAdapter);
|
|
4219
|
+
return storedResult.result;
|
|
2364
4220
|
}
|
|
2365
4221
|
/**
|
|
2366
4222
|
* Start a parse job and return immediately after the URL job is created or
|
|
@@ -2403,12 +4259,152 @@ var Knowhere = class {
|
|
|
2403
4259
|
return job;
|
|
2404
4260
|
}
|
|
2405
4261
|
};
|
|
4262
|
+
|
|
4263
|
+
// src/storage/disk-parsed-document-storage.ts
|
|
4264
|
+
import { createHash as createHash3 } from "crypto";
|
|
4265
|
+
import { promises as fs4 } from "fs";
|
|
4266
|
+
import path3 from "path";
|
|
4267
|
+
var DiskParsedDocumentStorage = class {
|
|
4268
|
+
rootDirectory;
|
|
4269
|
+
constructor(rootDirectory) {
|
|
4270
|
+
this.rootDirectory = rootDirectory;
|
|
4271
|
+
}
|
|
4272
|
+
async readManifest(params) {
|
|
4273
|
+
return this.readOptionalJson(
|
|
4274
|
+
this.getManifestPath(params.documentId, params.revisionKey)
|
|
4275
|
+
);
|
|
4276
|
+
}
|
|
4277
|
+
async writeManifest(params) {
|
|
4278
|
+
await this.writeJson(
|
|
4279
|
+
this.getManifestPath(params.documentId, params.revisionKey),
|
|
4280
|
+
params.manifest
|
|
4281
|
+
);
|
|
4282
|
+
}
|
|
4283
|
+
async readChunkPage(params) {
|
|
4284
|
+
return this.readOptionalJson(
|
|
4285
|
+
this.getChunkPagePath(params.documentId, params.revisionKey, params.page)
|
|
4286
|
+
);
|
|
4287
|
+
}
|
|
4288
|
+
async writeChunkPage(params) {
|
|
4289
|
+
await this.writeJson(
|
|
4290
|
+
this.getChunkPagePath(params.documentId, params.revisionKey, params.page.page),
|
|
4291
|
+
params.page
|
|
4292
|
+
);
|
|
4293
|
+
}
|
|
4294
|
+
async writeAsset(params) {
|
|
4295
|
+
const assetPath = this.getAssetPath(params.documentId, params.revisionKey, params.sourcePath);
|
|
4296
|
+
await fs4.mkdir(path3.dirname(assetPath), { recursive: true });
|
|
4297
|
+
await fs4.writeFile(assetPath, params.body);
|
|
4298
|
+
await this.writeJson(`${assetPath}.metadata.json`, {
|
|
4299
|
+
contentType: params.contentType,
|
|
4300
|
+
metadata: params.metadata ?? {}
|
|
4301
|
+
});
|
|
4302
|
+
return {
|
|
4303
|
+
sourcePath: params.sourcePath,
|
|
4304
|
+
url: toFileUrl(assetPath)
|
|
4305
|
+
};
|
|
4306
|
+
}
|
|
4307
|
+
async getAssetUrl(params) {
|
|
4308
|
+
const assetPath = this.getAssetPath(params.documentId, params.revisionKey, params.sourcePath);
|
|
4309
|
+
try {
|
|
4310
|
+
await fs4.access(assetPath);
|
|
4311
|
+
return toFileUrl(assetPath);
|
|
4312
|
+
} catch (error) {
|
|
4313
|
+
if (isMissingFileError3(error)) {
|
|
4314
|
+
return null;
|
|
4315
|
+
}
|
|
4316
|
+
throw error;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
async readSyncProgress(params) {
|
|
4320
|
+
return this.readOptionalJson(
|
|
4321
|
+
this.getProgressPath(params.documentId, params.revisionKey)
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
async writeSyncProgress(params) {
|
|
4325
|
+
await this.writeJson(this.getProgressPath(params.documentId, params.revisionKey), params);
|
|
4326
|
+
}
|
|
4327
|
+
getManifestPath(documentId, revisionKey) {
|
|
4328
|
+
return path3.join(
|
|
4329
|
+
this.getRevisionDirectory(documentId, revisionKey),
|
|
4330
|
+
"manifest",
|
|
4331
|
+
"current.json"
|
|
4332
|
+
);
|
|
4333
|
+
}
|
|
4334
|
+
getChunkPagePath(documentId, revisionKey, page) {
|
|
4335
|
+
return path3.join(
|
|
4336
|
+
this.getRevisionDirectory(documentId, revisionKey),
|
|
4337
|
+
"chunks",
|
|
4338
|
+
`page-${page}.json`
|
|
4339
|
+
);
|
|
4340
|
+
}
|
|
4341
|
+
getProgressPath(documentId, revisionKey) {
|
|
4342
|
+
return path3.join(this.getRevisionDirectory(documentId, revisionKey), "sync-progress.json");
|
|
4343
|
+
}
|
|
4344
|
+
getAssetPath(documentId, revisionKey, sourcePath) {
|
|
4345
|
+
const normalizedSourcePath = normalizeRelativeStoragePath(sourcePath);
|
|
4346
|
+
const assetPath = path3.resolve(
|
|
4347
|
+
this.getRevisionDirectory(documentId, revisionKey),
|
|
4348
|
+
"assets",
|
|
4349
|
+
normalizedSourcePath
|
|
4350
|
+
);
|
|
4351
|
+
const assetsDirectory = path3.resolve(
|
|
4352
|
+
this.getRevisionDirectory(documentId, revisionKey),
|
|
4353
|
+
"assets"
|
|
4354
|
+
);
|
|
4355
|
+
if (!isPathInsideDirectory2(assetPath, assetsDirectory)) {
|
|
4356
|
+
throw new Error(`Parsed asset path resolves outside storage: ${sourcePath}`);
|
|
4357
|
+
}
|
|
4358
|
+
return assetPath;
|
|
4359
|
+
}
|
|
4360
|
+
getRevisionDirectory(documentId, revisionKey) {
|
|
4361
|
+
return path3.join(this.rootDirectory, hashPathPart(documentId), hashPathPart(revisionKey));
|
|
4362
|
+
}
|
|
4363
|
+
async readOptionalJson(filePath) {
|
|
4364
|
+
try {
|
|
4365
|
+
return JSON.parse(await fs4.readFile(filePath, "utf8"));
|
|
4366
|
+
} catch (error) {
|
|
4367
|
+
if (isMissingFileError3(error)) {
|
|
4368
|
+
return null;
|
|
4369
|
+
}
|
|
4370
|
+
throw error;
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
async writeJson(filePath, value) {
|
|
4374
|
+
await fs4.mkdir(path3.dirname(filePath), { recursive: true });
|
|
4375
|
+
await fs4.writeFile(filePath, JSON.stringify(value, null, 2), {
|
|
4376
|
+
encoding: "utf8",
|
|
4377
|
+
flag: "w"
|
|
4378
|
+
});
|
|
4379
|
+
}
|
|
4380
|
+
};
|
|
4381
|
+
function hashPathPart(value) {
|
|
4382
|
+
return createHash3("sha256").update(value).digest("hex").slice(0, 24);
|
|
4383
|
+
}
|
|
4384
|
+
function normalizeRelativeStoragePath(value) {
|
|
4385
|
+
const normalized = value.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
4386
|
+
if (normalized.length === 0 || normalized.includes("\0") || normalized.startsWith("/") || normalized.split("/").some((part) => part.length === 0 || part === "." || part === "..")) {
|
|
4387
|
+
throw new Error(`Invalid parsed storage path: ${value}`);
|
|
4388
|
+
}
|
|
4389
|
+
return normalized;
|
|
4390
|
+
}
|
|
4391
|
+
function isPathInsideDirectory2(filePath, directory) {
|
|
4392
|
+
const relative = path3.relative(directory, filePath);
|
|
4393
|
+
return relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative);
|
|
4394
|
+
}
|
|
4395
|
+
function toFileUrl(filePath) {
|
|
4396
|
+
return `file://${filePath}`;
|
|
4397
|
+
}
|
|
4398
|
+
function isMissingFileError3(error) {
|
|
4399
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
4400
|
+
}
|
|
2406
4401
|
export {
|
|
2407
4402
|
APIError,
|
|
2408
4403
|
AuthenticationError,
|
|
2409
4404
|
BadRequestError,
|
|
2410
4405
|
ChecksumError,
|
|
2411
4406
|
ConflictError,
|
|
4407
|
+
DiskParsedDocumentStorage,
|
|
2412
4408
|
Documents,
|
|
2413
4409
|
GatewayTimeoutError,
|
|
2414
4410
|
InternalServerError,
|
|
@@ -2430,5 +4426,7 @@ export {
|
|
|
2430
4426
|
TimeoutError,
|
|
2431
4427
|
VERSION,
|
|
2432
4428
|
ValidationError,
|
|
2433
|
-
Knowhere as default
|
|
4429
|
+
Knowhere as default,
|
|
4430
|
+
storeParseResultAssets,
|
|
4431
|
+
syncParseResultToParsedDocumentStorage
|
|
2434
4432
|
};
|