@ontos-ai/knowhere-sdk 2.6.1-beta.20261027150736 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  syncParseResultToParsedDocumentStorage,
48
48
  syncRemoteResultArchive,
49
49
  validateArchiveSyncLimits
50
- } from "./chunk-DIONXJWK.mjs";
50
+ } from "./chunk-OCR7IDYL.mjs";
51
51
 
52
52
  // src/client.ts
53
53
  import path2 from "path";
@@ -1068,6 +1068,12 @@ var Jobs = class extends BaseResource {
1068
1068
  enrichJobResult(jobResult);
1069
1069
  return jobResult;
1070
1070
  }
1071
+ /** Renew unfinished enhancement without creating another ingestion. */
1072
+ async retryEnhancement(jobId) {
1073
+ return this.httpClient.post(
1074
+ this.endpoint(`/jobs/${jobId}/enrichment/retry`)
1075
+ );
1076
+ }
1071
1077
  /**
1072
1078
  * Upload file for a job
1073
1079
  */
@@ -1764,6 +1770,21 @@ function isMissingFileError2(error) {
1764
1770
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1765
1771
  }
1766
1772
 
1773
+ // src/knowledge/serialize-snapshot-publication.ts
1774
+ var pendingPublications = /* @__PURE__ */ new WeakMap();
1775
+ async function serializeSnapshotPublication(storage, documentId, publish) {
1776
+ const publications = pendingPublications.get(storage) ?? /* @__PURE__ */ new Map();
1777
+ pendingPublications.set(storage, publications);
1778
+ const previous = publications.get(documentId) ?? Promise.resolve();
1779
+ const current = previous.catch(() => void 0).then(publish);
1780
+ publications.set(documentId, current);
1781
+ try {
1782
+ return await current;
1783
+ } finally {
1784
+ if (publications.get(documentId) === current) publications.delete(documentId);
1785
+ }
1786
+ }
1787
+
1767
1788
  // src/knowledge/knowledge.ts
1768
1789
  var DEFAULT_READ_LIMIT = 12;
1769
1790
  var MAX_READ_LIMIT = 40;
@@ -1881,11 +1902,37 @@ var Knowledge = class _Knowledge {
1881
1902
  return this.importJobResult(params);
1882
1903
  }
1883
1904
  async syncParsedDocument(params) {
1905
+ const response = await this.performParsedDocumentSync(params);
1906
+ if (response.completed && !response.snapshot) {
1907
+ const storage = this.requireParsedStorageConfig().storage;
1908
+ await serializeSnapshotPublication(storage, response.documentId, async () => {
1909
+ if (params.documentId || params.jobId) {
1910
+ const current = await this.client.documents.listChunks(
1911
+ response.documentId,
1912
+ { page: 1, pageSize: 1 }
1913
+ );
1914
+ if (params.jobId && current.jobId !== params.jobId) return;
1915
+ if (!params.jobId && current.jobResultId !== response.revisionKey) return;
1916
+ }
1917
+ await storage.writeObject({
1918
+ documentId: response.documentId,
1919
+ revisionKey: "current",
1920
+ path: ".knowhere-sdk/current-snapshot.json",
1921
+ body: Buffer.from(
1922
+ JSON.stringify({ snapshotId: response.revisionKey, jobResultId: response.revisionKey })
1923
+ ),
1924
+ contentType: "application/json; charset=utf-8"
1925
+ });
1926
+ });
1927
+ }
1928
+ return response;
1929
+ }
1930
+ async performParsedDocumentSync(params) {
1884
1931
  params.signal?.throwIfAborted();
1885
1932
  const normalized = normalizeDocumentReference(params);
1886
1933
  const parsedStorageConfig = this.requireParsedStorageConfig();
1887
1934
  validateArchiveSyncLimits(parsedStorageConfig.limits);
1888
- if (normalized.documentId) {
1935
+ if (normalized.documentId && !normalized.jobId) {
1889
1936
  return this.syncRemoteDocumentToParsedStorage({
1890
1937
  documentId: normalized.documentId,
1891
1938
  revisionKey: params.revisionKey,
@@ -1897,6 +1944,25 @@ var Knowledge = class _Knowledge {
1897
1944
  }
1898
1945
  if (normalized.jobId) {
1899
1946
  const job = await this.client.jobs.get(normalized.jobId);
1947
+ if (job.snapshots?.length) {
1948
+ return this.syncJobSnapshot({
1949
+ job,
1950
+ documentId: normalized.documentId ?? job.documentId ?? normalized.localDocumentId ?? normalized.jobId,
1951
+ revisionKey: params.revisionKey,
1952
+ parsedStorageConfig,
1953
+ signal: params.signal,
1954
+ retainResultArchive: params.retainResultArchive
1955
+ });
1956
+ }
1957
+ if (normalized.documentId) {
1958
+ return this.syncRemoteDocumentToParsedStorage({
1959
+ documentId: normalized.documentId,
1960
+ revisionKey: params.revisionKey,
1961
+ parsedStorageConfig,
1962
+ signal: params.signal,
1963
+ retainResultArchive: params.retainResultArchive
1964
+ });
1965
+ }
1900
1966
  if (!job.isDone || !job.resultUrl)
1901
1967
  throw new ValidationError("Completed job result archive is unavailable");
1902
1968
  return syncRemoteResultArchive({
@@ -1928,6 +1994,89 @@ var Knowledge = class _Knowledge {
1928
1994
  commit: synced.commit
1929
1995
  };
1930
1996
  }
1997
+ async syncJobSnapshot(params) {
1998
+ const snapshotId = params.revisionKey ?? params.job.currentSnapshotId;
1999
+ const snapshot = params.job.snapshots?.find(
2000
+ (candidate) => candidate.snapshotId === snapshotId
2001
+ );
2002
+ if (!params.job.isDone || !snapshot)
2003
+ throw new ValidationError("Requested immutable parsed snapshot is unavailable");
2004
+ if (snapshot.jobId !== params.job.jobId || snapshot.documentId !== params.documentId)
2005
+ throw new ValidationError(
2006
+ "Parsed snapshot document or job identity differs from the request"
2007
+ );
2008
+ const response = await syncRemoteResultArchive({
2009
+ documentId: params.documentId,
2010
+ revisionKey: snapshot.snapshotId,
2011
+ jobId: snapshot.jobId,
2012
+ snapshot,
2013
+ resultUrl: snapshot.resultUrl,
2014
+ storage: params.parsedStorageConfig.storage,
2015
+ limits: params.parsedStorageConfig.limits,
2016
+ signal: params.signal,
2017
+ retainResultArchive: params.retainResultArchive
2018
+ });
2019
+ if (response.completed && snapshot.snapshotId === params.job.currentSnapshotId) {
2020
+ await serializeSnapshotPublication(
2021
+ params.parsedStorageConfig.storage,
2022
+ params.documentId,
2023
+ async () => {
2024
+ const current = await this.client.documents.listChunks(
2025
+ params.documentId,
2026
+ { page: 1, pageSize: 1 }
2027
+ );
2028
+ if (current.jobResultId !== snapshot.jobResultId) return;
2029
+ const pointer = Buffer.from(
2030
+ JSON.stringify({
2031
+ snapshotId: snapshot.snapshotId,
2032
+ jobResultId: snapshot.jobResultId
2033
+ })
2034
+ );
2035
+ if (snapshot.stage === "final") {
2036
+ await params.parsedStorageConfig.storage.writeObject({
2037
+ documentId: params.documentId,
2038
+ revisionKey: "current",
2039
+ path: ".knowhere-sdk/final-snapshot.json",
2040
+ body: pointer,
2041
+ contentType: "application/json; charset=utf-8"
2042
+ });
2043
+ }
2044
+ await params.parsedStorageConfig.storage.writeObject({
2045
+ documentId: params.documentId,
2046
+ revisionKey: "current",
2047
+ path: ".knowhere-sdk/current-snapshot.json",
2048
+ body: pointer,
2049
+ contentType: "application/json; charset=utf-8"
2050
+ });
2051
+ }
2052
+ );
2053
+ }
2054
+ return response;
2055
+ }
2056
+ async resolveStoredSnapshotKey(documentId) {
2057
+ const object = await this.parsedStorageConfig?.storage.readObject({
2058
+ documentId,
2059
+ revisionKey: "current",
2060
+ path: ".knowhere-sdk/current-snapshot.json"
2061
+ });
2062
+ if (!object) return void 0;
2063
+ const payload = JSON.parse(Buffer.from(object.body).toString("utf8"));
2064
+ if (typeof payload !== "object" || payload === null || !("snapshotId" in payload))
2065
+ throw new ValidationError("Invalid current parsed snapshot pointer");
2066
+ if (typeof payload.snapshotId !== "string")
2067
+ throw new ValidationError("Invalid current parsed snapshot identity");
2068
+ const finalObject = await this.parsedStorageConfig?.storage.readObject({
2069
+ documentId,
2070
+ revisionKey: "current",
2071
+ path: ".knowhere-sdk/final-snapshot.json"
2072
+ });
2073
+ if (finalObject && "jobResultId" in payload) {
2074
+ const finalPayload = JSON.parse(Buffer.from(finalObject.body).toString("utf8"));
2075
+ if (typeof finalPayload === "object" && finalPayload !== null && "snapshotId" in finalPayload && typeof finalPayload.snapshotId === "string" && "jobResultId" in finalPayload && finalPayload.jobResultId === payload.jobResultId)
2076
+ return finalPayload.snapshotId;
2077
+ }
2078
+ return payload.snapshotId;
2079
+ }
1931
2080
  async syncParseResultToConfiguredStorage(result) {
1932
2081
  const parsedStorageConfig = this.parsedStorageConfig;
1933
2082
  if (!parsedStorageConfig) {
@@ -2163,7 +2312,7 @@ var Knowledge = class _Knowledge {
2163
2312
  throw new Error(`Local Knowhere document not found: ${normalized.localDocumentId}`);
2164
2313
  }
2165
2314
  async readRemoteDocumentChunks(documentId, params) {
2166
- const explicitRevisionKey = params.revisionKey ?? params.jobId;
2315
+ const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
2167
2316
  if (explicitRevisionKey) {
2168
2317
  const stored2 = await this.tryReadChunksFromParsedStorage({
2169
2318
  documentId,
@@ -2173,6 +2322,8 @@ var Knowledge = class _Knowledge {
2173
2322
  if (stored2) {
2174
2323
  return stored2;
2175
2324
  }
2325
+ if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
2326
+ throw new ValidationError("Requested parsed snapshot is not committed locally");
2176
2327
  }
2177
2328
  if (hasScanReadFilters(params)) {
2178
2329
  return this.scanRemoteDocumentChunks(documentId, params);
@@ -2185,6 +2336,8 @@ var Knowledge = class _Knowledge {
2185
2336
  includeAssetUrls: true
2186
2337
  });
2187
2338
  const revisionKey = getRemoteRevisionKey(remotePage);
2339
+ if (params.revisionKey && params.revisionKey !== revisionKey)
2340
+ throw new ValidationError("Requested parsed snapshot revision is not committed locally");
2188
2341
  const stored = await this.tryReadChunksFromParsedStorage({
2189
2342
  documentId,
2190
2343
  revisionKey,
@@ -2209,7 +2362,7 @@ var Knowledge = class _Knowledge {
2209
2362
  };
2210
2363
  }
2211
2364
  async scanRemoteDocumentChunks(documentId, params) {
2212
- const explicitRevisionKey = params.revisionKey ?? params.jobId;
2365
+ const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
2213
2366
  if (explicitRevisionKey) {
2214
2367
  const stored2 = await this.tryReadChunksFromParsedStorage({
2215
2368
  documentId,
@@ -2219,6 +2372,8 @@ var Knowledge = class _Knowledge {
2219
2372
  if (stored2) {
2220
2373
  return stored2;
2221
2374
  }
2375
+ if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
2376
+ throw new ValidationError("Requested parsed snapshot is not committed locally");
2222
2377
  }
2223
2378
  const limit = clampLimit(params.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
2224
2379
  const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
@@ -2229,6 +2384,8 @@ var Knowledge = class _Knowledge {
2229
2384
  includeAssetUrls: true
2230
2385
  });
2231
2386
  const revisionKey = getRemoteRevisionKey(firstPage);
2387
+ if (params.revisionKey && params.revisionKey !== revisionKey)
2388
+ throw new ValidationError("Requested parsed snapshot revision is not committed locally");
2232
2389
  const stored = await this.tryReadChunksFromParsedStorage({
2233
2390
  documentId,
2234
2391
  revisionKey,
@@ -2271,7 +2428,7 @@ var Knowledge = class _Knowledge {
2271
2428
  };
2272
2429
  }
2273
2430
  async grepRemoteDocumentChunks(documentId, params) {
2274
- const explicitRevisionKey = params.revisionKey ?? params.jobId;
2431
+ const explicitRevisionKey = params.revisionKey ?? params.jobId ?? await this.resolveStoredSnapshotKey(documentId);
2275
2432
  if (explicitRevisionKey && !params.continuationCursor) {
2276
2433
  const stored2 = await this.tryGrepChunksFromParsedStorage({
2277
2434
  documentId,
@@ -2281,6 +2438,8 @@ var Knowledge = class _Knowledge {
2281
2438
  if (stored2) {
2282
2439
  return stored2;
2283
2440
  }
2441
+ if (params.revisionKey && await this.resolveStoredSnapshotKey(documentId))
2442
+ throw new ValidationError("Requested parsed snapshot is not committed locally");
2284
2443
  }
2285
2444
  const maxResults = clampLimit(params.maxResults, DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
2286
2445
  const contextChars = params.contextChars ?? DEFAULT_CONTEXT_CHARS;
@@ -2295,6 +2454,8 @@ var Knowledge = class _Knowledge {
2295
2454
  includeAssetUrls: true
2296
2455
  });
2297
2456
  const revisionKey = getRemoteRevisionKey(firstPage);
2457
+ if (params.revisionKey && params.revisionKey !== revisionKey)
2458
+ throw new ValidationError("Requested parsed snapshot revision is not committed locally");
2298
2459
  const stored = startPage === 1 ? await this.tryGrepChunksFromParsedStorage({
2299
2460
  documentId,
2300
2461
  revisionKey,
@@ -2380,11 +2541,14 @@ var Knowledge = class _Knowledge {
2380
2541
  }
2381
2542
  }
2382
2543
  async getRemoteDocumentOutline(documentId, revisionKeyOverride) {
2383
- if (revisionKeyOverride) {
2384
- const stored2 = await this.tryReadOutlineFromParsedStorage(documentId, revisionKeyOverride);
2544
+ const storedRevisionKey = revisionKeyOverride ?? await this.resolveStoredSnapshotKey(documentId);
2545
+ if (storedRevisionKey) {
2546
+ const stored2 = await this.tryReadOutlineFromParsedStorage(documentId, storedRevisionKey);
2385
2547
  if (stored2) {
2386
2548
  return stored2;
2387
2549
  }
2550
+ if (revisionKeyOverride && await this.resolveStoredSnapshotKey(documentId))
2551
+ throw new ValidationError("Requested parsed snapshot is not committed locally");
2388
2552
  }
2389
2553
  const limits = getParsedStorageLimits(this.parsedStorageConfig?.limits);
2390
2554
  const firstPage = await this.client.documents.listChunks(documentId, {
@@ -2393,6 +2557,8 @@ var Knowledge = class _Knowledge {
2393
2557
  includeAssetUrls: true
2394
2558
  });
2395
2559
  const revisionKey = getRemoteRevisionKey(firstPage);
2560
+ if (revisionKeyOverride && revisionKeyOverride !== revisionKey)
2561
+ throw new ValidationError("Requested parsed snapshot revision is not committed locally");
2396
2562
  const stored = await this.tryReadOutlineFromParsedStorage(documentId, revisionKey);
2397
2563
  if (stored) {
2398
2564
  return stored;
@@ -2601,11 +2767,7 @@ var Knowledge = class _Knowledge {
2601
2767
  const scheduler = parsedStorageConfig.scheduler ?? defaultParsedStorageScheduler;
2602
2768
  const task = async () => {
2603
2769
  try {
2604
- await this.syncRemoteDocumentToParsedStorage({
2605
- documentId,
2606
- revisionKey,
2607
- parsedStorageConfig
2608
- });
2770
+ await this.syncParsedDocument({ documentId, revisionKey });
2609
2771
  } catch (error) {
2610
2772
  const previous = await parsedStorageConfig.storage.readSyncProgress({
2611
2773
  documentId,
@@ -2631,14 +2793,25 @@ var Knowledge = class _Knowledge {
2631
2793
  includeAssetUrls: true
2632
2794
  });
2633
2795
  const revisionKey = params.revisionKey ?? getRemoteRevisionKey(firstPage);
2634
- if (params.revisionKey && getRemoteRevisionKey(firstPage) !== params.revisionKey) {
2635
- throw new ValidationError("Published document revision changed before parsed snapshot sync");
2636
- }
2637
2796
  if (firstPage.jobId) {
2638
2797
  const job = await this.client.jobs.get(firstPage.jobId).catch((error) => {
2639
2798
  if (error instanceof NotFoundError) return null;
2640
2799
  throw error;
2641
2800
  });
2801
+ if (job?.snapshots?.length) {
2802
+ const snapshot = job.snapshots.find(
2803
+ (candidate) => candidate.snapshotId === (params.revisionKey ?? job.currentSnapshotId)
2804
+ );
2805
+ if (snapshot && snapshot.jobResultId !== getRemoteRevisionKey(firstPage))
2806
+ throw new ValidationError(
2807
+ "Parsed snapshot upstream revision differs from the published document"
2808
+ );
2809
+ return this.syncJobSnapshot({ ...params, job });
2810
+ }
2811
+ if (params.revisionKey && getRemoteRevisionKey(firstPage) !== params.revisionKey)
2812
+ throw new ValidationError(
2813
+ "Published document revision changed before parsed snapshot sync"
2814
+ );
2642
2815
  if (job?.isDone && job.resultUrl) {
2643
2816
  if (job.documentId && job.documentId !== params.documentId)
2644
2817
  throw new ValidationError("Parsed job document identity differs from requested document");
@@ -3,9 +3,10 @@ import {
3
3
  createDownloadIdentity,
4
4
  isSnapshotPath,
5
5
  normalizeResultPath
6
- } from "./chunk-DIONXJWK.mjs";
6
+ } from "./chunk-OCR7IDYL.mjs";
7
7
 
8
8
  // src/storage/sequential-parsed-document-archive.ts
9
+ import { createHash } from "crypto";
9
10
  import { Readable, Transform } from "stream";
10
11
  import { pipeline } from "stream/promises";
11
12
  import { mkdtemp, open, rm } from "fs/promises";
@@ -15,7 +16,10 @@ import { crc32 } from "zlib";
15
16
  import { ZipReader, Reader } from "@zip.js/zip.js";
16
17
  import Parse from "unzipper/lib/parse.js";
17
18
  async function openSequentialArchive(params, identity, validateResponse) {
18
- let response = await fetch(params.resultUrl, { signal: params.signal });
19
+ let response = await fetch(params.resultUrl, {
20
+ signal: params.signal,
21
+ headers: validateResponse ? { Range: "bytes=0-" } : void 0
22
+ });
19
23
  if (!response.ok || !response.body)
20
24
  throw new Error(`Parsed result download failed (HTTP ${response.status}).`);
21
25
  if (validateResponse) response = validateResponse(response);
@@ -44,9 +48,11 @@ async function openSequentialArchive(params, identity, validateResponse) {
44
48
  const parser = Parse({ forceStream: true });
45
49
  const checksums = /* @__PURE__ */ new Map();
46
50
  let archiveSize = 0;
51
+ const archiveHash = createHash("sha256");
47
52
  const spool = new Transform({
48
53
  transform(chunk, _encoding, callback) {
49
54
  archiveSize += chunk.length;
55
+ archiveHash.update(chunk);
50
56
  void file.writeFile(chunk).then(
51
57
  () => {
52
58
  callback(null, chunk);
@@ -63,6 +69,23 @@ async function openSequentialArchive(params, identity, validateResponse) {
63
69
  identity,
64
70
  downloadIdentity: identity,
65
71
  canSkipEntries: false,
72
+ readArchive: () => {
73
+ let position = 0;
74
+ return new ReadableStream({
75
+ async pull(controller) {
76
+ params.signal?.throwIfAborted();
77
+ if (position >= archiveSize) {
78
+ controller.close();
79
+ return;
80
+ }
81
+ const body2 = new Uint8Array(Math.min(65536, archiveSize - position));
82
+ const { bytesRead } = await file.read(body2, 0, body2.length, position);
83
+ if (bytesRead !== body2.length) throw new Error("Truncated verified archive spool.");
84
+ position += bytesRead;
85
+ controller.enqueue(body2);
86
+ }
87
+ });
88
+ },
66
89
  entries: (async function* () {
67
90
  for await (const value of parser) {
68
91
  const entry = value;
@@ -93,6 +116,8 @@ async function openSequentialArchive(params, identity, validateResponse) {
93
116
  };
94
117
  }
95
118
  await completed;
119
+ if (params.snapshot && archiveHash.digest("hex") !== params.snapshot.checksum)
120
+ throw new Error("Parsed snapshot archive checksum mismatch for extracted bytes.");
96
121
  const reader = new ZipReader(new DiskArchiveReader(file, archiveSize), {
97
122
  useWebWorkers: false
98
123
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontos-ai/knowhere-sdk",
3
- "version": "2.6.1-beta.20261027150736",
3
+ "version": "2.7.0",
4
4
  "description": "Official Node.js SDK for Knowhere document parsing API",
5
5
  "keywords": [
6
6
  "knowhere",