@ontos-ai/knowhere-sdk 2.0.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 +98 -21
- package/dist/index.d.mts +707 -381
- package/dist/index.d.ts +707 -381
- package/dist/index.js +2009 -60
- package/dist/index.mjs +2005 -59
- 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,8 +696,8 @@ var BaseResource = class {
|
|
|
696
696
|
constructor(httpClient) {
|
|
697
697
|
this.httpClient = httpClient;
|
|
698
698
|
}
|
|
699
|
-
endpoint(
|
|
700
|
-
return `/v2${
|
|
699
|
+
endpoint(path4) {
|
|
700
|
+
return `/v2${path4.startsWith("/") ? path4 : `/${path4}`}`;
|
|
701
701
|
}
|
|
702
702
|
};
|
|
703
703
|
|
|
@@ -913,17 +913,28 @@ async function saveExpandedParseResult(result, directory) {
|
|
|
913
913
|
if (result.rawZip.length > 0) {
|
|
914
914
|
const didExtractZip = await tryExtractRawZip(result.rawZip, directory);
|
|
915
915
|
if (didExtractZip) {
|
|
916
|
+
await writeExpandedParseResultMetadata(result, directory);
|
|
916
917
|
return directory;
|
|
917
918
|
}
|
|
918
919
|
}
|
|
919
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) {
|
|
920
931
|
await fs2.writeFile(join(directory, "manifest.json"), JSON.stringify(result.manifest, null, 2));
|
|
921
932
|
if (result.docNav) {
|
|
922
933
|
await fs2.writeFile(join(directory, "doc_nav.json"), JSON.stringify(result.docNav, null, 2));
|
|
923
934
|
}
|
|
924
935
|
await fs2.writeFile(
|
|
925
936
|
join(directory, "chunks.json"),
|
|
926
|
-
JSON.stringify(serializeChunks(result.chunks), null, 2)
|
|
937
|
+
JSON.stringify(keysToSnake(serializeChunks(result.chunks)), null, 2)
|
|
927
938
|
);
|
|
928
939
|
if (result.chunksSlim) {
|
|
929
940
|
await fs2.writeFile(
|
|
@@ -952,13 +963,6 @@ async function saveExpandedParseResult(result, directory) {
|
|
|
952
963
|
if (result.hierarchyViewHtml) {
|
|
953
964
|
await fs2.writeFile(join(directory, "hierarchy_view.html"), result.hierarchyViewHtml);
|
|
954
965
|
}
|
|
955
|
-
for (const imageChunk of result.imageChunks) {
|
|
956
|
-
await writeBinaryAsset(directory, imageChunk.filePath, imageChunk.data);
|
|
957
|
-
}
|
|
958
|
-
for (const tableChunk of result.tableChunks) {
|
|
959
|
-
await writeTextAsset(directory, tableChunk.filePath, tableChunk.html);
|
|
960
|
-
}
|
|
961
|
-
return directory;
|
|
962
966
|
}
|
|
963
967
|
async function tryExtractRawZip(zipBuffer, directory) {
|
|
964
968
|
try {
|
|
@@ -1097,13 +1101,14 @@ function buildTextChunk(chunkData) {
|
|
|
1097
1101
|
};
|
|
1098
1102
|
}
|
|
1099
1103
|
function buildPageChunk(chunkData) {
|
|
1104
|
+
const metadata = chunkData.metadata ?? {};
|
|
1100
1105
|
return {
|
|
1101
1106
|
chunkId: chunkData.chunkId ?? "",
|
|
1102
1107
|
type: "page",
|
|
1103
1108
|
contentSource: chunkData.contentSource ?? "summary",
|
|
1104
1109
|
content: chunkData.content ?? "",
|
|
1105
1110
|
path: chunkData.path ?? "",
|
|
1106
|
-
metadata
|
|
1111
|
+
metadata
|
|
1107
1112
|
};
|
|
1108
1113
|
}
|
|
1109
1114
|
function buildImageChunk(chunkData, filePath, imageBuffer) {
|
|
@@ -1114,6 +1119,7 @@ function buildImageChunk(chunkData, filePath, imageBuffer) {
|
|
|
1114
1119
|
content: chunkData.content ?? "",
|
|
1115
1120
|
path: chunkData.path ?? "",
|
|
1116
1121
|
filePath,
|
|
1122
|
+
assetUrl: chunkData.assetUrl,
|
|
1117
1123
|
data: imageBuffer,
|
|
1118
1124
|
metadata: chunkData.metadata ?? {},
|
|
1119
1125
|
get format() {
|
|
@@ -1132,6 +1138,7 @@ function buildTableChunk(chunkData, filePath, html) {
|
|
|
1132
1138
|
content: chunkData.content ?? "",
|
|
1133
1139
|
path: chunkData.path ?? "",
|
|
1134
1140
|
filePath,
|
|
1141
|
+
assetUrl: chunkData.assetUrl,
|
|
1135
1142
|
html,
|
|
1136
1143
|
metadata: chunkData.metadata ?? {},
|
|
1137
1144
|
async save(directory) {
|
|
@@ -1226,6 +1233,7 @@ function serializeChunks(chunks) {
|
|
|
1226
1233
|
};
|
|
1227
1234
|
if (chunk.type === "image" || chunk.type === "table") {
|
|
1228
1235
|
rawChunk.filePath = chunk.filePath;
|
|
1236
|
+
rawChunk.assetUrl = chunk.assetUrl;
|
|
1229
1237
|
}
|
|
1230
1238
|
return rawChunk;
|
|
1231
1239
|
})
|
|
@@ -1434,6 +1442,14 @@ var Documents = class extends BaseResource {
|
|
|
1434
1442
|
this.createChunkGetRequestConfig(params)
|
|
1435
1443
|
);
|
|
1436
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
|
+
}
|
|
1437
1453
|
/**
|
|
1438
1454
|
* Archive one canonical document by ID.
|
|
1439
1455
|
*/
|
|
@@ -1506,6 +1522,514 @@ var Documents = class extends BaseResource {
|
|
|
1506
1522
|
// src/knowledge/knowledge.ts
|
|
1507
1523
|
import { createHash as createHash2 } from "crypto";
|
|
1508
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
|
+
|
|
1509
2033
|
// src/knowledge/local-store.ts
|
|
1510
2034
|
import { createHash } from "crypto";
|
|
1511
2035
|
import os from "os";
|
|
@@ -1731,27 +2255,62 @@ function isMissingFileError2(error) {
|
|
|
1731
2255
|
// src/knowledge/knowledge.ts
|
|
1732
2256
|
var DEFAULT_READ_LIMIT = 12;
|
|
1733
2257
|
var MAX_READ_LIMIT = 40;
|
|
2258
|
+
var DEFAULT_PAGE_SIZE = 20;
|
|
2259
|
+
var MAX_PAGE_SIZE = 200;
|
|
1734
2260
|
var DEFAULT_GREP_LIMIT = 20;
|
|
1735
2261
|
var MAX_GREP_LIMIT = 50;
|
|
1736
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;
|
|
1737
2271
|
var SAFE_LOCAL_DOCUMENT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1738
2272
|
var REMOTE_DOCUMENT_ID_PATTERN = /^doc[_-]/;
|
|
1739
2273
|
var Knowledge = class _Knowledge {
|
|
1740
2274
|
client;
|
|
1741
2275
|
store;
|
|
2276
|
+
cacheDirectory;
|
|
2277
|
+
parsedStorageConfig;
|
|
1742
2278
|
constructor(client, options) {
|
|
1743
2279
|
this.client = client;
|
|
2280
|
+
this.cacheDirectory = options?.cacheDirectory;
|
|
2281
|
+
this.parsedStorageConfig = options?.parsedStorage;
|
|
1744
2282
|
this.store = new LocalKnowledgeStore(options?.cacheDirectory);
|
|
1745
2283
|
}
|
|
1746
2284
|
withCacheDirectory(cacheDirectory) {
|
|
1747
|
-
return new _Knowledge(this.client, {
|
|
2285
|
+
return new _Knowledge(this.client, {
|
|
2286
|
+
cacheDirectory,
|
|
2287
|
+
parsedStorage: this.parsedStorageConfig
|
|
2288
|
+
});
|
|
1748
2289
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
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;
|
|
1751
2301
|
const document = await this.store.saveResult(result, {
|
|
1752
2302
|
localDocumentId: params.localDocumentId
|
|
1753
2303
|
});
|
|
1754
|
-
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);
|
|
1755
2314
|
}
|
|
1756
2315
|
async startParse(params) {
|
|
1757
2316
|
const job = await this.client.startParse(params);
|
|
@@ -1782,14 +2341,100 @@ var Knowledge = class _Knowledge {
|
|
|
1782
2341
|
results
|
|
1783
2342
|
};
|
|
1784
2343
|
}
|
|
1785
|
-
async
|
|
1786
|
-
const
|
|
2344
|
+
async loadJobResult(params) {
|
|
2345
|
+
const loadedResult = await this.client.jobs.load(params.jobId, {
|
|
1787
2346
|
verifyChecksum: params.verifyChecksum
|
|
1788
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;
|
|
1789
2360
|
const document = await this.store.saveResult(result, {
|
|
1790
2361
|
localDocumentId: params.localDocumentId
|
|
1791
2362
|
});
|
|
1792
|
-
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);
|
|
1793
2438
|
}
|
|
1794
2439
|
async cacheDocument(params) {
|
|
1795
2440
|
if (!params.documentId) {
|
|
@@ -1799,7 +2444,7 @@ var Knowledge = class _Knowledge {
|
|
|
1799
2444
|
params.documentId
|
|
1800
2445
|
);
|
|
1801
2446
|
const jobId = await this.getPublishedDocumentJobId(params.documentId);
|
|
1802
|
-
return this.
|
|
2447
|
+
return this.importJobResult({
|
|
1803
2448
|
jobId,
|
|
1804
2449
|
localDocumentId: params.localDocumentId ?? existing?.localDocumentId ?? createLocalDocumentIdForRemote(params.documentId)
|
|
1805
2450
|
});
|
|
@@ -1836,7 +2481,7 @@ var Knowledge = class _Knowledge {
|
|
|
1836
2481
|
};
|
|
1837
2482
|
}
|
|
1838
2483
|
try {
|
|
1839
|
-
const cached = await this.
|
|
2484
|
+
const cached = await this.importJobResult({
|
|
1840
2485
|
jobId,
|
|
1841
2486
|
localDocumentId: trackedJob.localDocumentId
|
|
1842
2487
|
});
|
|
@@ -1861,6 +2506,13 @@ var Knowledge = class _Knowledge {
|
|
|
1861
2506
|
return this.store.listDocuments();
|
|
1862
2507
|
}
|
|
1863
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
|
+
}
|
|
1864
2516
|
const { document, result } = await this.loadReadableResult(reference);
|
|
1865
2517
|
const chunks = indexChunks(result);
|
|
1866
2518
|
const sections = buildFlatSections(result, chunks);
|
|
@@ -1876,6 +2528,14 @@ var Knowledge = class _Knowledge {
|
|
|
1876
2528
|
};
|
|
1877
2529
|
}
|
|
1878
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
|
+
}
|
|
1879
2539
|
const { document, result } = await this.loadReadableResult(params);
|
|
1880
2540
|
const limit = clampLimit(params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
|
|
1881
2541
|
const chunks = indexChunks(result).filter((chunk) => matchesReadScope(chunk, params));
|
|
@@ -1892,6 +2552,13 @@ var Knowledge = class _Knowledge {
|
|
|
1892
2552
|
if (!params.pattern) {
|
|
1893
2553
|
throw new ValidationError("pattern is required");
|
|
1894
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
|
+
}
|
|
1895
2562
|
const { document, result } = await this.loadReadableResult(params);
|
|
1896
2563
|
const maxResults = clampLimit(params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
1897
2564
|
const contextChars = params.contextChars ?? DEFAULT_CONTEXT_CHARS;
|
|
@@ -1971,7 +2638,7 @@ var Knowledge = class _Knowledge {
|
|
|
1971
2638
|
});
|
|
1972
2639
|
}
|
|
1973
2640
|
if (normalized.jobId) {
|
|
1974
|
-
return this.
|
|
2641
|
+
return this.importJobResult({
|
|
1975
2642
|
jobId: normalized.jobId,
|
|
1976
2643
|
localDocumentId: normalized.localDocumentId
|
|
1977
2644
|
});
|
|
@@ -1991,31 +2658,677 @@ var Knowledge = class _Knowledge {
|
|
|
1991
2658
|
}
|
|
1992
2659
|
throw new Error(`Local Knowhere document not found: ${normalized.localDocumentId}`);
|
|
1993
2660
|
}
|
|
1994
|
-
async
|
|
1995
|
-
const
|
|
1996
|
-
|
|
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
|
+
};
|
|
1997
2714
|
}
|
|
1998
|
-
async
|
|
1999
|
-
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, {
|
|
2000
2730
|
page: 1,
|
|
2001
|
-
pageSize:
|
|
2731
|
+
pageSize: limits.remotePageSize,
|
|
2732
|
+
chunkType: params.chunkType,
|
|
2733
|
+
includeAssetUrls: shouldRequestRemoteAssetUrls(
|
|
2734
|
+
params.assetUrlPolicy,
|
|
2735
|
+
this.parsedStorageConfig
|
|
2736
|
+
)
|
|
2002
2737
|
});
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2738
|
+
const revisionKey = getRemoteRevisionKey(firstPage);
|
|
2739
|
+
const stored = await this.tryReadChunksFromParsedStorage({
|
|
2740
|
+
documentId,
|
|
2741
|
+
revisionKey,
|
|
2742
|
+
params
|
|
2743
|
+
});
|
|
2744
|
+
if (stored) {
|
|
2745
|
+
return stored;
|
|
2007
2746
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
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
|
+
};
|
|
2014
2787
|
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
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) {
|
|
3325
|
+
if (typeof reference === "string") {
|
|
3326
|
+
return { localDocumentId: reference };
|
|
3327
|
+
}
|
|
3328
|
+
return reference;
|
|
3329
|
+
}
|
|
3330
|
+
function looksLikeRemoteDocumentId(value) {
|
|
3331
|
+
return REMOTE_DOCUMENT_ID_PATTERN.test(value);
|
|
2019
3332
|
}
|
|
2020
3333
|
function createLocalDocumentIdForRemote(documentId) {
|
|
2021
3334
|
if (SAFE_LOCAL_DOCUMENT_ID_PATTERN.test(documentId) && !documentId.includes("..") && !documentId.includes("/") && !documentId.includes("\\")) {
|
|
@@ -2024,6 +3337,488 @@ function createLocalDocumentIdForRemote(documentId) {
|
|
|
2024
3337
|
const hash = createHash2("sha256").update(documentId).digest("hex").slice(0, 16);
|
|
2025
3338
|
return `remote_${hash}`;
|
|
2026
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
|
+
}
|
|
2027
3822
|
function indexChunks(result) {
|
|
2028
3823
|
return result.chunks.map((chunk, index) => {
|
|
2029
3824
|
const filePath = getChunkFilePath2(chunk);
|
|
@@ -2039,6 +3834,7 @@ function indexChunks(result) {
|
|
|
2039
3834
|
sectionPath: normalizeSectionPath(chunk.path, result.manifest.sourceFileName),
|
|
2040
3835
|
sourceChunkPath: chunk.path,
|
|
2041
3836
|
filePath,
|
|
3837
|
+
assetUrl: getChunkAssetUrl(chunk),
|
|
2042
3838
|
pageNumbers: getChunkPageNumbers(chunk.metadata),
|
|
2043
3839
|
metadata: chunk.metadata
|
|
2044
3840
|
};
|
|
@@ -2052,12 +3848,11 @@ function getReadableChunkContent(chunk) {
|
|
|
2052
3848
|
return typeof summary === "string" && summary.trim().length > 0 ? summary : chunk.content;
|
|
2053
3849
|
}
|
|
2054
3850
|
function getChunkPageNumbers(metadata) {
|
|
2055
|
-
const
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
(pageNumber) => Number.isInteger(pageNumber) && pageNumber > 0
|
|
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
|
+
)
|
|
2061
3856
|
);
|
|
2062
3857
|
return pageNumbers.length > 0 ? [...new Set(pageNumbers)].sort((left, right) => left - right) : void 0;
|
|
2063
3858
|
}
|
|
@@ -2068,14 +3863,17 @@ function getChunkFilePath2(chunk) {
|
|
|
2068
3863
|
const filePath = chunk.metadata.filePath;
|
|
2069
3864
|
return typeof filePath === "string" ? filePath : void 0;
|
|
2070
3865
|
}
|
|
2071
|
-
function
|
|
2072
|
-
|
|
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) {
|
|
2073
3871
|
return "";
|
|
2074
3872
|
}
|
|
2075
|
-
if (
|
|
2076
|
-
return
|
|
3873
|
+
if (path4.startsWith("images/") || path4.startsWith("tables/")) {
|
|
3874
|
+
return path4;
|
|
2077
3875
|
}
|
|
2078
|
-
const parts =
|
|
3876
|
+
const parts = path4.split("/").filter(Boolean);
|
|
2079
3877
|
if (sourceFileName) {
|
|
2080
3878
|
const fileNameIndex = parts.indexOf(sourceFileName);
|
|
2081
3879
|
if (fileNameIndex >= 0) {
|
|
@@ -2097,12 +3895,12 @@ function buildFlatSections(result, chunks) {
|
|
|
2097
3895
|
}
|
|
2098
3896
|
const byPath = /* @__PURE__ */ new Map();
|
|
2099
3897
|
for (const chunk of chunks) {
|
|
2100
|
-
const
|
|
2101
|
-
const existing = byPath.get(
|
|
3898
|
+
const path4 = chunk.sectionPath || chunk.sourceChunkPath;
|
|
3899
|
+
const existing = byPath.get(path4);
|
|
2102
3900
|
if (existing) {
|
|
2103
3901
|
addChunkToSection(existing, chunk);
|
|
2104
3902
|
} else {
|
|
2105
|
-
byPath.set(
|
|
3903
|
+
byPath.set(path4, createSectionFromChunk(path4, chunk));
|
|
2106
3904
|
}
|
|
2107
3905
|
}
|
|
2108
3906
|
return [...byPath.values()].sort(compareSections);
|
|
@@ -2236,6 +4034,7 @@ function toReadChunk(chunk) {
|
|
|
2236
4034
|
sectionPath: chunk.sectionPath,
|
|
2237
4035
|
sourceChunkPath: chunk.sourceChunkPath,
|
|
2238
4036
|
filePath: chunk.filePath,
|
|
4037
|
+
assetUrl: chunk.assetUrl,
|
|
2239
4038
|
pageNumbers: chunk.pageNumbers,
|
|
2240
4039
|
metadata: chunk.metadata
|
|
2241
4040
|
};
|
|
@@ -2412,7 +4211,12 @@ var Knowhere = class {
|
|
|
2412
4211
|
const result = await this.jobs.load(jobResult, {
|
|
2413
4212
|
verifyChecksum: params.verifyChecksum
|
|
2414
4213
|
});
|
|
2415
|
-
|
|
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;
|
|
2416
4220
|
}
|
|
2417
4221
|
/**
|
|
2418
4222
|
* Start a parse job and return immediately after the URL job is created or
|
|
@@ -2455,12 +4259,152 @@ var Knowhere = class {
|
|
|
2455
4259
|
return job;
|
|
2456
4260
|
}
|
|
2457
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
|
+
}
|
|
2458
4401
|
export {
|
|
2459
4402
|
APIError,
|
|
2460
4403
|
AuthenticationError,
|
|
2461
4404
|
BadRequestError,
|
|
2462
4405
|
ChecksumError,
|
|
2463
4406
|
ConflictError,
|
|
4407
|
+
DiskParsedDocumentStorage,
|
|
2464
4408
|
Documents,
|
|
2465
4409
|
GatewayTimeoutError,
|
|
2466
4410
|
InternalServerError,
|
|
@@ -2482,5 +4426,7 @@ export {
|
|
|
2482
4426
|
TimeoutError,
|
|
2483
4427
|
VERSION,
|
|
2484
4428
|
ValidationError,
|
|
2485
|
-
Knowhere as default
|
|
4429
|
+
Knowhere as default,
|
|
4430
|
+
storeParseResultAssets,
|
|
4431
|
+
syncParseResultToParsedDocumentStorage
|
|
2486
4432
|
};
|