wolbarg 0.5.3 → 0.5.4
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 +1 -1
- package/dist/index.cjs +874 -373
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +874 -373
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -662,6 +662,45 @@ function withEmbeddingCache(provider, store, config) {
|
|
|
662
662
|
};
|
|
663
663
|
}
|
|
664
664
|
|
|
665
|
+
// src/utils/vector.ts
|
|
666
|
+
function cosineDistance(a, b) {
|
|
667
|
+
if (a.length !== b.length) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`cosineDistance dimension mismatch: a.length=${a.length}, b.length=${b.length}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
const len = a.length;
|
|
673
|
+
let dot = 0;
|
|
674
|
+
let normA = 0;
|
|
675
|
+
let normB = 0;
|
|
676
|
+
for (let i = 0; i < len; i += 1) {
|
|
677
|
+
const av = a[i] ?? 0;
|
|
678
|
+
const bv = b[i] ?? 0;
|
|
679
|
+
dot += av * bv;
|
|
680
|
+
normA += av * av;
|
|
681
|
+
normB += bv * bv;
|
|
682
|
+
}
|
|
683
|
+
if (normA === 0 || normB === 0) {
|
|
684
|
+
return 1;
|
|
685
|
+
}
|
|
686
|
+
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
687
|
+
return 1 - similarity;
|
|
688
|
+
}
|
|
689
|
+
function bufferToEmbedding(data) {
|
|
690
|
+
const byteOffset = data.byteOffset ?? 0;
|
|
691
|
+
const byteLength = data.byteLength;
|
|
692
|
+
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
693
|
+
return new Float32Array(
|
|
694
|
+
data.buffer,
|
|
695
|
+
byteOffset,
|
|
696
|
+
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const copy = new Uint8Array(byteLength);
|
|
700
|
+
copy.set(data);
|
|
701
|
+
return new Float32Array(copy.buffer);
|
|
702
|
+
}
|
|
703
|
+
|
|
665
704
|
// src/embedding/cache-store.ts
|
|
666
705
|
var TOUCH_FLUSH_THRESHOLD = 64;
|
|
667
706
|
var L1_DEFAULT_MAX = 5e4;
|
|
@@ -746,7 +785,9 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
746
785
|
}
|
|
747
786
|
this.stmts = {
|
|
748
787
|
get: db.prepare(
|
|
749
|
-
`SELECT vector, last_used_at, created_at
|
|
788
|
+
`SELECT model, vector, last_used_at, created_at
|
|
789
|
+
FROM embedding_cache
|
|
790
|
+
WHERE cache_key = ?`
|
|
750
791
|
),
|
|
751
792
|
delete: db.prepare(`DELETE FROM embedding_cache WHERE cache_key = ?`),
|
|
752
793
|
set: db.prepare(
|
|
@@ -781,7 +822,29 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
781
822
|
this.l1.set(cacheKey, pending.model, pending.vector);
|
|
782
823
|
return pending.vector;
|
|
783
824
|
}
|
|
784
|
-
|
|
825
|
+
const db = this.requireDb();
|
|
826
|
+
if (!db) return null;
|
|
827
|
+
try {
|
|
828
|
+
const stmts = this.ensureStatements(db);
|
|
829
|
+
const row = stmts.get.get(cacheKey);
|
|
830
|
+
if (!row) return null;
|
|
831
|
+
if (this.ttlMs !== null) {
|
|
832
|
+
const createdMs = row.created_at ? Date.parse(row.created_at) : 0;
|
|
833
|
+
if (createdMs && Date.now() - createdMs > this.ttlMs) {
|
|
834
|
+
stmts.delete.run(cacheKey);
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const vector = bufferToEmbedding(row.vector);
|
|
839
|
+
this.l1.set(cacheKey, row.model, vector);
|
|
840
|
+
this.pendingTouches.add(cacheKey);
|
|
841
|
+
if (this.pendingTouches.size >= TOUCH_FLUSH_THRESHOLD) {
|
|
842
|
+
this.schedulePersist();
|
|
843
|
+
}
|
|
844
|
+
return vector;
|
|
845
|
+
} catch {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
785
848
|
}
|
|
786
849
|
async set(cacheKey, model, vector) {
|
|
787
850
|
this.l1.set(cacheKey, model, vector);
|
|
@@ -833,6 +896,16 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
833
896
|
this.pendingSets.clear();
|
|
834
897
|
const touches = [...this.pendingTouches];
|
|
835
898
|
this.pendingTouches.clear();
|
|
899
|
+
const requeue = () => {
|
|
900
|
+
for (const [key, value] of sets) {
|
|
901
|
+
this.pendingSets.set(key, value);
|
|
902
|
+
}
|
|
903
|
+
for (const key of touches) {
|
|
904
|
+
this.pendingTouches.add(key);
|
|
905
|
+
}
|
|
906
|
+
this.stmts = null;
|
|
907
|
+
this.schedulePersist();
|
|
908
|
+
};
|
|
836
909
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
837
910
|
try {
|
|
838
911
|
const stmts = this.ensureStatements(db);
|
|
@@ -867,10 +940,10 @@ var SqliteEmbeddingCacheStore = class {
|
|
|
867
940
|
db.exec("ROLLBACK");
|
|
868
941
|
} catch {
|
|
869
942
|
}
|
|
870
|
-
|
|
943
|
+
requeue();
|
|
871
944
|
}
|
|
872
945
|
} catch {
|
|
873
|
-
|
|
946
|
+
requeue();
|
|
874
947
|
}
|
|
875
948
|
}
|
|
876
949
|
};
|
|
@@ -1179,12 +1252,27 @@ var DocxParser = class {
|
|
|
1179
1252
|
name = "docx";
|
|
1180
1253
|
extensions = [".docx"];
|
|
1181
1254
|
async parse(input) {
|
|
1255
|
+
let mod;
|
|
1256
|
+
try {
|
|
1257
|
+
mod = await import('mammoth');
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
throw new ConfigurationError(
|
|
1260
|
+
'DOCX ingest requires the optional peer package "mammoth". Install it with: npm install mammoth',
|
|
1261
|
+
{
|
|
1262
|
+
cause: error instanceof Error ? error : void 0,
|
|
1263
|
+
suggestion: "npm install mammoth",
|
|
1264
|
+
operation: "parse"
|
|
1265
|
+
}
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1269
|
+
if (!extractRawText) {
|
|
1270
|
+
throw new ConfigurationError(
|
|
1271
|
+
"DOCX ingest could not find mammoth.extractRawText. Ensure your mammoth version is compatible.",
|
|
1272
|
+
{ operation: "parse" }
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1182
1275
|
try {
|
|
1183
|
-
const mod = await import('mammoth');
|
|
1184
|
-
const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
|
|
1185
|
-
if (!extractRawText) {
|
|
1186
|
-
throw new Error("mammoth.extractRawText unavailable");
|
|
1187
|
-
}
|
|
1188
1276
|
const result = await extractRawText({ buffer: input.buffer });
|
|
1189
1277
|
return {
|
|
1190
1278
|
text: result.value ?? "",
|
|
@@ -1192,9 +1280,14 @@ var DocxParser = class {
|
|
|
1192
1280
|
filename: input.filename,
|
|
1193
1281
|
isImage: false
|
|
1194
1282
|
};
|
|
1195
|
-
} catch {
|
|
1283
|
+
} catch (error) {
|
|
1196
1284
|
throw new ConfigurationError(
|
|
1197
|
-
|
|
1285
|
+
"DOCX ingest failed to parse the document (it may be corrupt or an unsupported .docx).",
|
|
1286
|
+
{
|
|
1287
|
+
cause: error instanceof Error ? error : void 0,
|
|
1288
|
+
suggestion: "Try a different DOCX file or ensure it is not password-protected.",
|
|
1289
|
+
operation: "parse"
|
|
1290
|
+
}
|
|
1198
1291
|
);
|
|
1199
1292
|
}
|
|
1200
1293
|
}
|
|
@@ -1341,6 +1434,12 @@ function compileComparison(field, op) {
|
|
|
1341
1434
|
const extract = `json_extract(metadata_json, '${path8}')`;
|
|
1342
1435
|
if ("eq" in op) {
|
|
1343
1436
|
const value = op.eq;
|
|
1437
|
+
if (value === null) {
|
|
1438
|
+
return {
|
|
1439
|
+
expression: `json_type(metadata_json, '${path8}') = 'null'`,
|
|
1440
|
+
params: []
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1344
1443
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
1345
1444
|
return null;
|
|
1346
1445
|
}
|
|
@@ -1421,6 +1520,59 @@ function compileMetadataFilterToSql(filter) {
|
|
|
1421
1520
|
return compileComparison(filter.field, filter.op);
|
|
1422
1521
|
}
|
|
1423
1522
|
|
|
1523
|
+
// src/telemetry/logger.ts
|
|
1524
|
+
var LEVEL_ORDER = {
|
|
1525
|
+
off: 100,
|
|
1526
|
+
error: 50,
|
|
1527
|
+
warn: 40,
|
|
1528
|
+
info: 30,
|
|
1529
|
+
debug: 20,
|
|
1530
|
+
trace: 10
|
|
1531
|
+
};
|
|
1532
|
+
var WolbargLogger = class {
|
|
1533
|
+
constructor(level = "info") {
|
|
1534
|
+
this.level = level;
|
|
1535
|
+
}
|
|
1536
|
+
level;
|
|
1537
|
+
setLevel(level) {
|
|
1538
|
+
this.level = level;
|
|
1539
|
+
}
|
|
1540
|
+
getLevel() {
|
|
1541
|
+
return this.level;
|
|
1542
|
+
}
|
|
1543
|
+
error(message, extra) {
|
|
1544
|
+
this.write("error", message, extra);
|
|
1545
|
+
}
|
|
1546
|
+
warn(message, extra) {
|
|
1547
|
+
this.write("warn", message, extra);
|
|
1548
|
+
}
|
|
1549
|
+
info(message, extra) {
|
|
1550
|
+
this.write("info", message, extra);
|
|
1551
|
+
}
|
|
1552
|
+
debug(message, extra) {
|
|
1553
|
+
this.write("debug", message, extra);
|
|
1554
|
+
}
|
|
1555
|
+
trace(message, extra) {
|
|
1556
|
+
this.write("trace", message, extra);
|
|
1557
|
+
}
|
|
1558
|
+
write(level, message, extra) {
|
|
1559
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
if (this.level === "off") {
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const line = `[wolbarg:${level}] ${message}`;
|
|
1566
|
+
if (level === "error") {
|
|
1567
|
+
console.error(line, extra ?? "");
|
|
1568
|
+
} else if (level === "warn") {
|
|
1569
|
+
console.warn(line, extra ?? "");
|
|
1570
|
+
} else {
|
|
1571
|
+
console.log(line, extra ?? "");
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1424
1576
|
// src/schema/index.ts
|
|
1425
1577
|
var SCHEMA_VERSION = 4;
|
|
1426
1578
|
var META_KEYS = {
|
|
@@ -1735,40 +1887,6 @@ var SQL = {
|
|
|
1735
1887
|
`
|
|
1736
1888
|
};
|
|
1737
1889
|
|
|
1738
|
-
// src/utils/vector.ts
|
|
1739
|
-
function cosineDistance(a, b) {
|
|
1740
|
-
const len = Math.min(a.length, b.length);
|
|
1741
|
-
let dot = 0;
|
|
1742
|
-
let normA = 0;
|
|
1743
|
-
let normB = 0;
|
|
1744
|
-
for (let i = 0; i < len; i += 1) {
|
|
1745
|
-
const av = a[i] ?? 0;
|
|
1746
|
-
const bv = b[i] ?? 0;
|
|
1747
|
-
dot += av * bv;
|
|
1748
|
-
normA += av * av;
|
|
1749
|
-
normB += bv * bv;
|
|
1750
|
-
}
|
|
1751
|
-
if (normA === 0 || normB === 0) {
|
|
1752
|
-
return 1;
|
|
1753
|
-
}
|
|
1754
|
-
const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
1755
|
-
return 1 - similarity;
|
|
1756
|
-
}
|
|
1757
|
-
function bufferToEmbedding(data) {
|
|
1758
|
-
const byteOffset = data.byteOffset ?? 0;
|
|
1759
|
-
const byteLength = data.byteLength;
|
|
1760
|
-
if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
|
|
1761
|
-
return new Float32Array(
|
|
1762
|
-
data.buffer,
|
|
1763
|
-
byteOffset,
|
|
1764
|
-
byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
1765
|
-
);
|
|
1766
|
-
}
|
|
1767
|
-
const copy = new Uint8Array(byteLength);
|
|
1768
|
-
copy.set(data);
|
|
1769
|
-
return new Float32Array(copy.buffer);
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1890
|
// src/utils/vector-index.ts
|
|
1773
1891
|
var InMemoryVectorIndex = class {
|
|
1774
1892
|
dims;
|
|
@@ -1861,24 +1979,33 @@ var InMemoryVectorIndex = class {
|
|
|
1861
1979
|
dot += queryNormalized[d] * data[base + d];
|
|
1862
1980
|
}
|
|
1863
1981
|
const distance = 1 - dot;
|
|
1982
|
+
const rowid = rowids[i];
|
|
1864
1983
|
if (heapSize < k) {
|
|
1865
1984
|
heapDist[heapSize] = distance;
|
|
1866
|
-
heapRow[heapSize] =
|
|
1985
|
+
heapRow[heapSize] = rowid;
|
|
1867
1986
|
heapSize += 1;
|
|
1868
1987
|
if (heapSize === k) {
|
|
1869
1988
|
buildMaxHeap(heapDist, heapRow, k);
|
|
1870
1989
|
}
|
|
1871
|
-
} else
|
|
1872
|
-
heapDist[0]
|
|
1873
|
-
|
|
1874
|
-
|
|
1990
|
+
} else {
|
|
1991
|
+
const worstDist = heapDist[0];
|
|
1992
|
+
const worstRow = heapRow[0];
|
|
1993
|
+
if (distance < worstDist || distance === worstDist && rowid < worstRow) {
|
|
1994
|
+
heapDist[0] = distance;
|
|
1995
|
+
heapRow[0] = rowid;
|
|
1996
|
+
siftDown(heapDist, heapRow, 0, k);
|
|
1997
|
+
}
|
|
1875
1998
|
}
|
|
1876
1999
|
}
|
|
1877
2000
|
const hits = new Array(heapSize);
|
|
1878
2001
|
for (let i = 0; i < heapSize; i += 1) {
|
|
1879
2002
|
hits[i] = { memoryRowid: heapRow[i], distance: heapDist[i] };
|
|
1880
2003
|
}
|
|
1881
|
-
hits.sort((a, b) =>
|
|
2004
|
+
hits.sort((a, b) => {
|
|
2005
|
+
const diff = a.distance - b.distance;
|
|
2006
|
+
if (diff !== 0) return diff;
|
|
2007
|
+
return a.memoryRowid - b.memoryRowid;
|
|
2008
|
+
});
|
|
1882
2009
|
return hits;
|
|
1883
2010
|
}
|
|
1884
2011
|
ensureCapacity(needed) {
|
|
@@ -1924,8 +2051,8 @@ function siftDown(dist, rows, i, n) {
|
|
|
1924
2051
|
let largest = i;
|
|
1925
2052
|
const left = (i << 1) + 1;
|
|
1926
2053
|
const right = left + 1;
|
|
1927
|
-
if (left < n && dist[left] > dist[largest]) largest = left;
|
|
1928
|
-
if (right < n && dist[right] > dist[largest]) largest = right;
|
|
2054
|
+
if (left < n && (dist[left] > dist[largest] || dist[left] === dist[largest] && rows[left] > rows[largest])) largest = left;
|
|
2055
|
+
if (right < n && (dist[right] > dist[largest] || dist[right] === dist[largest] && rows[right] > rows[largest])) largest = right;
|
|
1929
2056
|
if (largest === i) return;
|
|
1930
2057
|
const td = dist[i];
|
|
1931
2058
|
dist[i] = dist[largest];
|
|
@@ -2067,6 +2194,9 @@ var BATCH_INSERT_CHUNK = 96;
|
|
|
2067
2194
|
var MAX_ANN_OVERFETCH = 8192;
|
|
2068
2195
|
var INSERT_COALESCE_THRESHOLD = 24;
|
|
2069
2196
|
var INSERT_COALESCE_MAX = 96;
|
|
2197
|
+
var warnLogger = new WolbargLogger("warn");
|
|
2198
|
+
var warnedSqliteVecFallback = false;
|
|
2199
|
+
var warnedFtsKeywordSearch = false;
|
|
2070
2200
|
var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
2071
2201
|
name = "sqlite";
|
|
2072
2202
|
connectionString;
|
|
@@ -2076,6 +2206,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2076
2206
|
vectorDimensions = null;
|
|
2077
2207
|
vectorBackend = null;
|
|
2078
2208
|
sqliteVecLoaded = false;
|
|
2209
|
+
/** Tracks nesting depth for {@link withTransaction} so we can use savepoints. */
|
|
2210
|
+
transactionDepth = 0;
|
|
2211
|
+
/** Incrementing counter for deterministic savepoint names. */
|
|
2212
|
+
savepointCounter = 0;
|
|
2079
2213
|
/** Hot in-process ANN for blob backend (sqlite-vec unavailable platforms). */
|
|
2080
2214
|
memoryIndex = null;
|
|
2081
2215
|
memoryIndexDirty = false;
|
|
@@ -2142,6 +2276,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2142
2276
|
} else if (this.sqliteVecLoaded) {
|
|
2143
2277
|
this.vectorBackend = "sqlite-vec";
|
|
2144
2278
|
} else {
|
|
2279
|
+
if (!warnedSqliteVecFallback) {
|
|
2280
|
+
warnedSqliteVecFallback = true;
|
|
2281
|
+
warnLogger.warn(
|
|
2282
|
+
"sqlite-vec unavailable on this platform; using blob ANN fallback."
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2145
2285
|
this.vectorBackend = "blob";
|
|
2146
2286
|
}
|
|
2147
2287
|
if (dims !== null) {
|
|
@@ -2385,6 +2525,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2385
2525
|
async searchKeyword(query, organization, topK) {
|
|
2386
2526
|
const stmts = this.requireStatements();
|
|
2387
2527
|
if (!stmts.searchFts) {
|
|
2528
|
+
if (!warnedFtsKeywordSearch) {
|
|
2529
|
+
warnedFtsKeywordSearch = true;
|
|
2530
|
+
warnLogger.warn(
|
|
2531
|
+
"FTS keyword search is unavailable; returning empty keyword hits."
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2388
2534
|
return [];
|
|
2389
2535
|
}
|
|
2390
2536
|
try {
|
|
@@ -2398,7 +2544,16 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2398
2544
|
// bm25 returns lower (more negative) for better matches — invert to [0, ∞)
|
|
2399
2545
|
score: 1 / (1 + Math.abs(row.rank))
|
|
2400
2546
|
}));
|
|
2401
|
-
} catch {
|
|
2547
|
+
} catch (error) {
|
|
2548
|
+
if (!warnedFtsKeywordSearch) {
|
|
2549
|
+
warnedFtsKeywordSearch = true;
|
|
2550
|
+
warnLogger.warn(
|
|
2551
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
2552
|
+
{
|
|
2553
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2554
|
+
}
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2402
2557
|
return [];
|
|
2403
2558
|
}
|
|
2404
2559
|
}
|
|
@@ -2629,16 +2784,39 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2629
2784
|
}
|
|
2630
2785
|
async withTransaction(fn) {
|
|
2631
2786
|
const db = this.requireDb();
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2787
|
+
if (this.transactionDepth > 0) {
|
|
2788
|
+
const savepointName = `wolbarg_sp_${this.savepointCounter++}`;
|
|
2789
|
+
db.exec(`SAVEPOINT ${savepointName}`);
|
|
2790
|
+
try {
|
|
2791
|
+
this.transactionDepth += 1;
|
|
2792
|
+
const result = await fn();
|
|
2793
|
+
db.exec(`RELEASE SAVEPOINT ${savepointName}`);
|
|
2794
|
+
return result;
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
try {
|
|
2797
|
+
db.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
|
|
2798
|
+
} catch {
|
|
2799
|
+
}
|
|
2800
|
+
throw error;
|
|
2801
|
+
} finally {
|
|
2802
|
+
this.transactionDepth -= 1;
|
|
2640
2803
|
}
|
|
2641
|
-
|
|
2804
|
+
}
|
|
2805
|
+
this.transactionDepth = 1;
|
|
2806
|
+
try {
|
|
2807
|
+
return await withImmediateTransaction(
|
|
2808
|
+
db,
|
|
2809
|
+
this.concurrency,
|
|
2810
|
+
fn,
|
|
2811
|
+
(attempt, delayMs) => {
|
|
2812
|
+
this.retryLog?.(
|
|
2813
|
+
`SQLITE_BUSY retry attempt=${attempt} backoffMs=${Math.round(delayMs)}`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
);
|
|
2817
|
+
} finally {
|
|
2818
|
+
this.transactionDepth = 0;
|
|
2819
|
+
}
|
|
2642
2820
|
}
|
|
2643
2821
|
// ─── internals ───────────────────────────────────────────────────────────
|
|
2644
2822
|
tryLoadSqliteVec(db) {
|
|
@@ -3792,9 +3970,13 @@ function createPostgresListenerFromPool(pool, onError) {
|
|
|
3792
3970
|
|
|
3793
3971
|
// src/storage/providers/postgres.ts
|
|
3794
3972
|
var txStore = new AsyncLocalStorage();
|
|
3973
|
+
var warnLogger2 = new WolbargLogger("warn");
|
|
3974
|
+
var warnedPgvectorFallback = false;
|
|
3975
|
+
var warnedFtsKeywordSearch2 = false;
|
|
3976
|
+
var warnedHnswSoftFail = false;
|
|
3795
3977
|
var STMT = {
|
|
3796
3978
|
insertOne: "Wolbarg_insert_one_v5",
|
|
3797
|
-
insertBatch: "
|
|
3979
|
+
insertBatch: "Wolbarg_insert_batch_v6"
|
|
3798
3980
|
};
|
|
3799
3981
|
function toFloat4Param(embedding) {
|
|
3800
3982
|
const out = new Array(embedding.length);
|
|
@@ -3865,13 +4047,13 @@ var INSERT_ONE_SQL = `WITH mem AS (
|
|
|
3865
4047
|
var INSERT_BATCH_SQL = `WITH mem AS (
|
|
3866
4048
|
INSERT INTO memories (
|
|
3867
4049
|
id, organization, agent, content_text, metadata_json,
|
|
3868
|
-
archived, compressed_into, created_at, updated_at
|
|
4050
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
3869
4051
|
)
|
|
3870
|
-
SELECT id, org, agent, txt, meta::jsonb, false, NULL, c, u
|
|
4052
|
+
SELECT id, org, agent, txt, meta::jsonb, false, NULL, h, c, u
|
|
3871
4053
|
FROM unnest(
|
|
3872
4054
|
$1::text[], $2::text[], $3::text[], $4::text[],
|
|
3873
|
-
$5::text[], $6::timestamptz[], $7::timestamptz[]
|
|
3874
|
-
) AS t(id, org, agent, txt, meta, c, u)
|
|
4055
|
+
$5::text[], $6::timestamptz[], $7::timestamptz[], $8::text[]
|
|
4056
|
+
) AS t(id, org, agent, txt, meta, c, u, h)
|
|
3875
4057
|
RETURNING id
|
|
3876
4058
|
),
|
|
3877
4059
|
hist AS (
|
|
@@ -3887,7 +4069,7 @@ var INSERT_BATCH_SQL = `WITH mem AS (
|
|
|
3887
4069
|
emb AS (
|
|
3888
4070
|
INSERT INTO memory_embeddings (memory_id, embedding, organization, agent, archived)
|
|
3889
4071
|
SELECT id, emb::vector, org, agent, false
|
|
3890
|
-
FROM unnest($1::text[], $
|
|
4072
|
+
FROM unnest($1::text[], $9::text[], $2::text[], $3::text[]) AS t(id, emb, org, agent)
|
|
3891
4073
|
)
|
|
3892
4074
|
SELECT id FROM mem`;
|
|
3893
4075
|
var COALESCE_FLUSH_MAX = 128;
|
|
@@ -4156,6 +4338,12 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4156
4338
|
return;
|
|
4157
4339
|
}
|
|
4158
4340
|
this.hnswCreateFailures += 1;
|
|
4341
|
+
if (this.hnswCreateFailures === 1 && !warnedHnswSoftFail) {
|
|
4342
|
+
warnedHnswSoftFail = true;
|
|
4343
|
+
warnLogger2.warn(
|
|
4344
|
+
"HNSW index creation failed; ANN will fall back to sequential scan."
|
|
4345
|
+
);
|
|
4346
|
+
}
|
|
4159
4347
|
if (this.hnswCreateFailures >= 3) {
|
|
4160
4348
|
throw new DatabaseError(
|
|
4161
4349
|
`Failed to create HNSW index after ${this.hnswCreateFailures} attempts: ${this.describe(error)}`,
|
|
@@ -4300,6 +4488,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4300
4488
|
const metas = new Array(inputs.length);
|
|
4301
4489
|
const created = new Array(inputs.length);
|
|
4302
4490
|
const updated = new Array(inputs.length);
|
|
4491
|
+
const contentHashes = new Array(inputs.length);
|
|
4303
4492
|
const vectors = new Array(inputs.length);
|
|
4304
4493
|
for (let i = 0; i < inputs.length; i += 1) {
|
|
4305
4494
|
const input = inputs[i];
|
|
@@ -4310,6 +4499,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4310
4499
|
metas[i] = serializeMetadata(input.metadata);
|
|
4311
4500
|
created[i] = input.createdAt;
|
|
4312
4501
|
updated[i] = input.updatedAt;
|
|
4502
|
+
contentHashes[i] = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4313
4503
|
vectors[i] = toVectorLiteral(input.embedding);
|
|
4314
4504
|
}
|
|
4315
4505
|
await this.queryNamed(STMT.insertBatch, INSERT_BATCH_SQL, [
|
|
@@ -4320,9 +4510,10 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4320
4510
|
metas,
|
|
4321
4511
|
created,
|
|
4322
4512
|
updated,
|
|
4513
|
+
contentHashes,
|
|
4323
4514
|
vectors
|
|
4324
4515
|
]);
|
|
4325
|
-
return inputs.map((
|
|
4516
|
+
return inputs.map((_, i) => ({
|
|
4326
4517
|
id: ids[i],
|
|
4327
4518
|
organization: orgs[i],
|
|
4328
4519
|
agent: agents[i],
|
|
@@ -4330,7 +4521,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4330
4521
|
metadata_json: metas[i],
|
|
4331
4522
|
archived: 0,
|
|
4332
4523
|
compressed_into: null,
|
|
4333
|
-
content_hash:
|
|
4524
|
+
content_hash: contentHashes[i] ?? null,
|
|
4334
4525
|
created_at: created[i],
|
|
4335
4526
|
updated_at: updated[i]
|
|
4336
4527
|
}));
|
|
@@ -4371,80 +4562,84 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4371
4562
|
return out;
|
|
4372
4563
|
}
|
|
4373
4564
|
async insertOneBlob(input) {
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4565
|
+
return this.withTransaction(async () => {
|
|
4566
|
+
const buf = Buffer.from(
|
|
4567
|
+
input.embedding.buffer,
|
|
4568
|
+
input.embedding.byteOffset,
|
|
4569
|
+
input.embedding.byteLength
|
|
4570
|
+
);
|
|
4571
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
|
|
4572
|
+
const inserted = await this.query(
|
|
4573
|
+
`INSERT INTO memories (
|
|
4574
|
+
id, organization, agent, content_text, metadata_json,
|
|
4575
|
+
archived, compressed_into, content_hash, created_at, updated_at
|
|
4576
|
+
) VALUES ($1,$2,$3,$4,$5::jsonb,false,NULL,$8,$6,$7)
|
|
4577
|
+
RETURNING id, organization, agent, content_text, metadata_json,
|
|
4578
|
+
archived::int AS archived, compressed_into, content_hash, created_at, updated_at`,
|
|
4579
|
+
[
|
|
4580
|
+
input.id,
|
|
4581
|
+
input.organization,
|
|
4582
|
+
input.agent,
|
|
4583
|
+
input.contentText,
|
|
4584
|
+
serializeMetadata(input.metadata),
|
|
4585
|
+
input.createdAt,
|
|
4586
|
+
input.updatedAt,
|
|
4587
|
+
contentHash
|
|
4588
|
+
]
|
|
4589
|
+
);
|
|
4590
|
+
const row = this.mapRow(inserted.rows[0]);
|
|
4591
|
+
await this.query(
|
|
4592
|
+
`WITH mapped AS (
|
|
4593
|
+
INSERT INTO memory_row_map (memory_id) VALUES ($1)
|
|
4594
|
+
ON CONFLICT (memory_id) DO NOTHING
|
|
4595
|
+
)
|
|
4596
|
+
INSERT INTO memory_embeddings_blob (memory_id, embedding)
|
|
4597
|
+
VALUES ($1, $2)
|
|
4598
|
+
ON CONFLICT (memory_id) DO UPDATE SET embedding = EXCLUDED.embedding`,
|
|
4599
|
+
[input.id, buf]
|
|
4600
|
+
);
|
|
4601
|
+
await this.query(
|
|
4602
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4603
|
+
VALUES ($1,$2,'created',NULL,$3)`,
|
|
4604
|
+
[crypto.randomUUID(), input.id, input.createdAt]
|
|
4605
|
+
);
|
|
4606
|
+
return row;
|
|
4607
|
+
});
|
|
4415
4608
|
}
|
|
4416
4609
|
async updateMemory(input) {
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4610
|
+
return this.withTransaction(async () => {
|
|
4611
|
+
const existing = await this.getMemoryById(input.id, input.organization);
|
|
4612
|
+
if (!existing) {
|
|
4613
|
+
return null;
|
|
4614
|
+
}
|
|
4615
|
+
const contentHash = input.contentHash !== void 0 ? input.contentHash : input.contentText !== void 0 ? hashMemoryContent(input.contentText) : existing.content_hash ?? null;
|
|
4616
|
+
await this.query(
|
|
4617
|
+
`UPDATE memories SET
|
|
4618
|
+
content_text = COALESCE($1, content_text),
|
|
4619
|
+
metadata_json = COALESCE($2::jsonb, metadata_json),
|
|
4620
|
+
content_hash = COALESCE($3, content_hash),
|
|
4621
|
+
updated_at = $4
|
|
4622
|
+
WHERE id = $5 AND organization = $6`,
|
|
4623
|
+
[
|
|
4624
|
+
input.contentText ?? null,
|
|
4625
|
+
input.metadata !== void 0 ? serializeMetadata(input.metadata) : null,
|
|
4626
|
+
contentHash,
|
|
4627
|
+
input.updatedAt,
|
|
4628
|
+
input.id,
|
|
4629
|
+
input.organization
|
|
4630
|
+
]
|
|
4631
|
+
);
|
|
4632
|
+
if (input.embedding) {
|
|
4633
|
+
await this.deleteEmbedding(input.id);
|
|
4634
|
+
await this.insertEmbedding(input.id, input.embedding);
|
|
4635
|
+
}
|
|
4636
|
+
await this.query(
|
|
4637
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4638
|
+
VALUES ($1,$2,'updated',NULL,$3)`,
|
|
4639
|
+
[crypto.randomUUID(), input.id, input.updatedAt]
|
|
4640
|
+
);
|
|
4641
|
+
return this.getMemoryById(input.id, input.organization);
|
|
4642
|
+
});
|
|
4448
4643
|
}
|
|
4449
4644
|
async findActiveByContentHash(organization, agent, contentHash) {
|
|
4450
4645
|
const result = await this.query(
|
|
@@ -4593,7 +4788,16 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4593
4788
|
memoryId: String(row.memory_id),
|
|
4594
4789
|
score: Number(row.rank)
|
|
4595
4790
|
}));
|
|
4596
|
-
} catch {
|
|
4791
|
+
} catch (error) {
|
|
4792
|
+
if (!warnedFtsKeywordSearch2) {
|
|
4793
|
+
warnedFtsKeywordSearch2 = true;
|
|
4794
|
+
warnLogger2.warn(
|
|
4795
|
+
"FTS keyword search failed; returning empty keyword hits.",
|
|
4796
|
+
{
|
|
4797
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
4798
|
+
}
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4597
4801
|
return [];
|
|
4598
4802
|
}
|
|
4599
4803
|
}
|
|
@@ -4711,44 +4915,46 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4711
4915
|
return mapHits(result.rows);
|
|
4712
4916
|
}
|
|
4713
4917
|
async archiveMemories(ids, organization, compressedIntoId, archivedAt) {
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4918
|
+
return this.withTransaction(async () => {
|
|
4919
|
+
if (ids.length === 0) {
|
|
4920
|
+
return [];
|
|
4921
|
+
}
|
|
4922
|
+
const result = await this.query(
|
|
4923
|
+
`UPDATE memories
|
|
4924
|
+
SET archived = true, compressed_into = $1, updated_at = $2
|
|
4925
|
+
WHERE organization = $3 AND archived = false AND id = ANY($4::text[])
|
|
4926
|
+
RETURNING id`,
|
|
4927
|
+
[compressedIntoId, archivedAt, organization, ids]
|
|
4928
|
+
);
|
|
4929
|
+
const archived = result.rows.map((r) => String(r.id));
|
|
4930
|
+
if (archived.length === 0) {
|
|
4931
|
+
return [];
|
|
4932
|
+
}
|
|
4933
|
+
await this.query(
|
|
4934
|
+
`UPDATE memory_embeddings
|
|
4935
|
+
SET archived = true
|
|
4936
|
+
WHERE memory_id = ANY($1::text[])`,
|
|
4937
|
+
[archived]
|
|
4938
|
+
);
|
|
4939
|
+
const histIds = [];
|
|
4940
|
+
const memIds = [];
|
|
4941
|
+
const types = [];
|
|
4942
|
+
const related = [];
|
|
4943
|
+
const times = [];
|
|
4944
|
+
for (const id of archived) {
|
|
4945
|
+
histIds.push(crypto.randomUUID(), crypto.randomUUID());
|
|
4946
|
+
memIds.push(id, compressedIntoId);
|
|
4947
|
+
types.push("archived", "compressed");
|
|
4948
|
+
related.push(compressedIntoId, id);
|
|
4949
|
+
times.push(archivedAt, archivedAt);
|
|
4950
|
+
}
|
|
4951
|
+
await this.query(
|
|
4952
|
+
`INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
|
|
4953
|
+
SELECT * FROM unnest($1::text[], $2::text[], $3::text[], $4::text[], $5::timestamptz[])`,
|
|
4954
|
+
[histIds, memIds, types, related, times]
|
|
4955
|
+
);
|
|
4956
|
+
return archived;
|
|
4957
|
+
});
|
|
4752
4958
|
}
|
|
4753
4959
|
async deleteMemoryById(id, organization) {
|
|
4754
4960
|
const result = await this.query(
|
|
@@ -4859,6 +5065,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
4859
5065
|
[META_KEYS.schemaVersion]
|
|
4860
5066
|
).catch(() => ({ rows: [] }));
|
|
4861
5067
|
const current = versionRow.rows[0]?.value !== void 0 ? Number(versionRow.rows[0].value) : null;
|
|
5068
|
+
if (current !== null && current > SCHEMA_VERSION) {
|
|
5069
|
+
throw new InitializationError(
|
|
5070
|
+
`Database schema version ${current} is newer than this Wolbarg SDK (${SCHEMA_VERSION}). Please upgrade Wolbarg to open this database.`
|
|
5071
|
+
);
|
|
5072
|
+
}
|
|
4862
5073
|
if (current === SCHEMA_VERSION) {
|
|
4863
5074
|
const tsvProbe2 = await this.query(
|
|
4864
5075
|
`SELECT 1 FROM information_schema.columns
|
|
@@ -5009,8 +5220,17 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
|
|
|
5009
5220
|
await this.query(`CREATE EXTENSION IF NOT EXISTS vector`);
|
|
5010
5221
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, true);
|
|
5011
5222
|
return true;
|
|
5012
|
-
} catch {
|
|
5223
|
+
} catch (error) {
|
|
5013
5224
|
_PostgresStorageProvider.pgvectorByConn.set(this.connectionString, false);
|
|
5225
|
+
if (!warnedPgvectorFallback) {
|
|
5226
|
+
warnedPgvectorFallback = true;
|
|
5227
|
+
warnLogger2.warn(
|
|
5228
|
+
"pgvector extension is unavailable; using blob cosine search fallback.",
|
|
5229
|
+
{
|
|
5230
|
+
cause: error instanceof Error ? error.message : `unknown error: ${String(error)}`
|
|
5231
|
+
}
|
|
5232
|
+
);
|
|
5233
|
+
}
|
|
5014
5234
|
return false;
|
|
5015
5235
|
}
|
|
5016
5236
|
}
|
|
@@ -5151,11 +5371,12 @@ function createDatabaseProvider(config) {
|
|
|
5151
5371
|
}
|
|
5152
5372
|
|
|
5153
5373
|
// src/version.ts
|
|
5154
|
-
var SDK_VERSION = "0.5.
|
|
5374
|
+
var SDK_VERSION = "0.5.4";
|
|
5155
5375
|
|
|
5156
5376
|
// src/memory/transfer.ts
|
|
5377
|
+
var warnedMissingExportManifest = false;
|
|
5157
5378
|
var SqliteMemoryTransferProvider = class {
|
|
5158
|
-
async exportTo(exportPath, sourcePath, organization) {
|
|
5379
|
+
async exportTo(exportPath, sourcePath, organization, embeddingModel, embeddingDimensions) {
|
|
5159
5380
|
const resolvedSource = resolvePath(sourcePath);
|
|
5160
5381
|
if (resolvedSource === ":memory:") {
|
|
5161
5382
|
throw new ConfigurationError(
|
|
@@ -5182,7 +5403,9 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5182
5403
|
sdkVersion: SDK_VERSION,
|
|
5183
5404
|
provider: "sqlite",
|
|
5184
5405
|
sourcePath: resolvedSource,
|
|
5185
|
-
organization
|
|
5406
|
+
organization,
|
|
5407
|
+
...embeddingModel ? { embeddingModel } : {},
|
|
5408
|
+
...embeddingDimensions ? { embeddingDimensions } : {}
|
|
5186
5409
|
};
|
|
5187
5410
|
fs6.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
5188
5411
|
return {
|
|
@@ -5191,7 +5414,7 @@ Initialize Wolbarg and verify the database path.`
|
|
|
5191
5414
|
sizeBytes: fs6.statSync(dbExportPath).size
|
|
5192
5415
|
};
|
|
5193
5416
|
}
|
|
5194
|
-
async importFrom(exportPath, targetPath) {
|
|
5417
|
+
async importFrom(exportPath, targetPath, expected) {
|
|
5195
5418
|
const resolvedExport = resolvePath(exportPath);
|
|
5196
5419
|
const dbExportPath = resolvedExport.endsWith(".db") ? resolvedExport : fs6.existsSync(`${resolvedExport}.db`) ? `${resolvedExport}.db` : resolvedExport;
|
|
5197
5420
|
if (!fs6.existsSync(dbExportPath)) {
|
|
@@ -5209,30 +5432,61 @@ Pass the path returned by export().`
|
|
|
5209
5432
|
manifest = JSON.parse(fs6.readFileSync(manifestPath, "utf8"));
|
|
5210
5433
|
if (manifest.format !== "wolbarg-export-v1") {
|
|
5211
5434
|
throw new ValidationError(
|
|
5212
|
-
`Unsupported export format: ${String(manifest.format)}`
|
|
5435
|
+
`Unsupported export format: ${String(manifest.format)}`
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
} else {
|
|
5439
|
+
if (!warnedMissingExportManifest) {
|
|
5440
|
+
warnedMissingExportManifest = true;
|
|
5441
|
+
console.warn(
|
|
5442
|
+
"[wolbarg:warn] export manifest is missing; skipping manifest-based import validation."
|
|
5443
|
+
);
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
if (manifest) {
|
|
5447
|
+
if (expected?.organization && manifest.organization && expected.organization !== manifest.organization) {
|
|
5448
|
+
throw new ValidationError(
|
|
5449
|
+
"Import failed: export organization does not match the current Wolbarg organization."
|
|
5450
|
+
);
|
|
5451
|
+
}
|
|
5452
|
+
if (expected?.embeddingDimensions != null && manifest.embeddingDimensions != null && expected.embeddingDimensions !== manifest.embeddingDimensions) {
|
|
5453
|
+
throw new ValidationError(
|
|
5454
|
+
`Import failed: embedding dimension mismatch (expected ${expected.embeddingDimensions}, got ${manifest.embeddingDimensions}).`
|
|
5455
|
+
);
|
|
5456
|
+
}
|
|
5457
|
+
if (expected?.embeddingModel && manifest.embeddingModel && expected.embeddingModel !== manifest.embeddingModel) {
|
|
5458
|
+
throw new ValidationError(
|
|
5459
|
+
`Import failed: embedding model mismatch (expected ${expected.embeddingModel}, got ${manifest.embeddingModel}).`
|
|
5213
5460
|
);
|
|
5214
5461
|
}
|
|
5215
|
-
} else {
|
|
5216
|
-
manifest = {
|
|
5217
|
-
format: "wolbarg-export-v1",
|
|
5218
|
-
exportedAt: nowIso(),
|
|
5219
|
-
sdkVersion: SDK_VERSION,
|
|
5220
|
-
provider: "sqlite",
|
|
5221
|
-
sourcePath: dbExportPath
|
|
5222
|
-
};
|
|
5223
5462
|
}
|
|
5224
5463
|
const resolvedTarget = resolvePath(targetPath);
|
|
5225
5464
|
if (resolvedTarget === ":memory:") {
|
|
5226
5465
|
throw new ConfigurationError("Cannot import into an in-memory database.");
|
|
5227
5466
|
}
|
|
5467
|
+
const tmpTarget = `${resolvedTarget}.tmp-import-${Date.now()}`;
|
|
5468
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5469
|
+
const side = `${tmpTarget}${suffix}`;
|
|
5470
|
+
if (fs6.existsSync(side)) {
|
|
5471
|
+
fs6.rmSync(side, { force: true });
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
await copySqlite(dbExportPath, tmpTarget);
|
|
5228
5475
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
5229
5476
|
const side = `${resolvedTarget}${suffix}`;
|
|
5230
5477
|
if (fs6.existsSync(side)) {
|
|
5231
5478
|
fs6.rmSync(side, { force: true });
|
|
5232
5479
|
}
|
|
5233
5480
|
}
|
|
5234
|
-
|
|
5235
|
-
|
|
5481
|
+
fs6.renameSync(tmpTarget, resolvedTarget);
|
|
5482
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
5483
|
+
const from = `${tmpTarget}${suffix}`;
|
|
5484
|
+
const to = `${resolvedTarget}${suffix}`;
|
|
5485
|
+
if (fs6.existsSync(from)) {
|
|
5486
|
+
fs6.renameSync(from, to);
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
return { path: resolvedTarget, manifest: manifest ?? void 0 };
|
|
5236
5490
|
}
|
|
5237
5491
|
};
|
|
5238
5492
|
async function copySqlite(sourcePath, destPath) {
|
|
@@ -5461,7 +5715,8 @@ function applyMmr(candidates, topK, lambda) {
|
|
|
5461
5715
|
);
|
|
5462
5716
|
}
|
|
5463
5717
|
const score = lambda * relevance - (1 - lambda) * maxSim;
|
|
5464
|
-
|
|
5718
|
+
const best = remaining[bestIdx];
|
|
5719
|
+
if (score > bestScore || score === bestScore && candidate.id < best.id) {
|
|
5465
5720
|
bestScore = score;
|
|
5466
5721
|
bestIdx = i;
|
|
5467
5722
|
}
|
|
@@ -6021,24 +6276,26 @@ var Neo4jGraphProvider = class {
|
|
|
6021
6276
|
}
|
|
6022
6277
|
}
|
|
6023
6278
|
async run(cypher, params = {}) {
|
|
6024
|
-
return this.withSession(
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6279
|
+
return this.withSession(
|
|
6280
|
+
(session) => this.runOnSession(session, cypher, params)
|
|
6281
|
+
);
|
|
6282
|
+
}
|
|
6283
|
+
async runOnSession(session, cypher, params = {}) {
|
|
6284
|
+
const result = await session.run(cypher, params);
|
|
6285
|
+
return result.records.map((rec) => {
|
|
6286
|
+
try {
|
|
6287
|
+
return rec.toObject();
|
|
6288
|
+
} catch {
|
|
6289
|
+
const obj = {};
|
|
6290
|
+
for (const key of rec.keys) {
|
|
6291
|
+
obj[key] = unwrapNeo4jValue(rec.get(key));
|
|
6035
6292
|
}
|
|
6036
|
-
|
|
6293
|
+
return obj;
|
|
6294
|
+
}
|
|
6037
6295
|
});
|
|
6038
6296
|
}
|
|
6039
|
-
async ensureMemoryNode(id) {
|
|
6040
|
-
|
|
6041
|
-
`MERGE (m:Memory { id: $id })
|
|
6297
|
+
async ensureMemoryNode(id, session) {
|
|
6298
|
+
const cypher = `MERGE (m:Memory { id: $id })
|
|
6042
6299
|
ON CREATE SET
|
|
6043
6300
|
m.organization = '',
|
|
6044
6301
|
m.agent = '',
|
|
@@ -6047,24 +6304,30 @@ var Neo4jGraphProvider = class {
|
|
|
6047
6304
|
m.archived = false,
|
|
6048
6305
|
m.compressed_into = '',
|
|
6049
6306
|
m.created_at = '',
|
|
6050
|
-
m.updated_at = ''
|
|
6051
|
-
|
|
6052
|
-
|
|
6307
|
+
m.updated_at = ''`;
|
|
6308
|
+
if (session) {
|
|
6309
|
+
await this.runOnSession(session, cypher, { id });
|
|
6310
|
+
return;
|
|
6311
|
+
}
|
|
6312
|
+
await this.run(cypher, { id });
|
|
6053
6313
|
}
|
|
6054
6314
|
async linkMemories(fromId, toId, relation, metadata) {
|
|
6055
|
-
await this.
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6315
|
+
await this.withSession(async (session) => {
|
|
6316
|
+
await this.ensureMemoryNode(fromId, session);
|
|
6317
|
+
await this.ensureMemoryNode(toId, session);
|
|
6318
|
+
await this.runOnSession(
|
|
6319
|
+
session,
|
|
6320
|
+
`MATCH (a:Memory { id: $fromId }), (b:Memory { id: $toId })
|
|
6321
|
+
MERGE (a)-[r:RELATED { relation: $relation }]->(b)
|
|
6322
|
+
SET r.metadata_json = $metadata`,
|
|
6323
|
+
{
|
|
6324
|
+
fromId,
|
|
6325
|
+
toId,
|
|
6326
|
+
relation,
|
|
6327
|
+
metadata: serializeMetadata2(metadata)
|
|
6328
|
+
}
|
|
6329
|
+
);
|
|
6330
|
+
});
|
|
6068
6331
|
}
|
|
6069
6332
|
async unlinkMemories(fromId, toId, relation) {
|
|
6070
6333
|
if (relation !== void 0) {
|
|
@@ -6126,22 +6389,26 @@ var Neo4jGraphProvider = class {
|
|
|
6126
6389
|
return id;
|
|
6127
6390
|
}
|
|
6128
6391
|
async linkEntityToMemory(entityId, memoryId, role) {
|
|
6129
|
-
await this.
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6392
|
+
await this.withSession(async (session) => {
|
|
6393
|
+
await this.ensureMemoryNode(memoryId, session);
|
|
6394
|
+
const found = await this.runOnSession(
|
|
6395
|
+
session,
|
|
6396
|
+
`MATCH (e:Entity { id: $id }) RETURN e.id AS id`,
|
|
6397
|
+
{ id: entityId }
|
|
6398
|
+
);
|
|
6399
|
+
if (found.length === 0) {
|
|
6400
|
+
throw new DatabaseError(`Entity not found: ${entityId}`, {
|
|
6401
|
+
operation: "linkEntityToMemory"
|
|
6402
|
+
});
|
|
6403
|
+
}
|
|
6404
|
+
await this.runOnSession(
|
|
6405
|
+
session,
|
|
6406
|
+
`MATCH (e:Entity { id: $entityId }), (m:Memory { id: $memoryId })
|
|
6407
|
+
MERGE (e)-[r:MENTIONS]->(m)
|
|
6408
|
+
SET r.role = $role`,
|
|
6409
|
+
{ entityId, memoryId, role: role ?? "" }
|
|
6410
|
+
);
|
|
6411
|
+
});
|
|
6145
6412
|
}
|
|
6146
6413
|
async deleteMemory(memoryId) {
|
|
6147
6414
|
await this.run(
|
|
@@ -6214,6 +6481,21 @@ function assertNonEmpty(value, fieldName) {
|
|
|
6214
6481
|
throw new ConfigurationError(`${fieldName} must be a non-empty string`);
|
|
6215
6482
|
}
|
|
6216
6483
|
}
|
|
6484
|
+
function assertFiniteNumber2(value, fieldName) {
|
|
6485
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6486
|
+
throw new ConfigurationError(`${fieldName} must be a finite number`);
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
function assertNumberMin(value, fieldName, min) {
|
|
6490
|
+
if (!Number.isFinite(value) || value < min) {
|
|
6491
|
+
throw new ConfigurationError(`${fieldName} must be >= ${min}`);
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
function assertNumberInRange(value, fieldName, min, max) {
|
|
6495
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
6496
|
+
throw new ConfigurationError(`${fieldName} must be between ${min} and ${max}`);
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6217
6499
|
function assertUrl(value, fieldName) {
|
|
6218
6500
|
assertNonEmpty(value, fieldName);
|
|
6219
6501
|
try {
|
|
@@ -6260,6 +6542,135 @@ function validateLlmConfig(config) {
|
|
|
6260
6542
|
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {}
|
|
6261
6543
|
};
|
|
6262
6544
|
}
|
|
6545
|
+
function validateRetrievalConfig(config) {
|
|
6546
|
+
if (config === null || typeof config !== "object") {
|
|
6547
|
+
throw new ConfigurationError("retrieval must be an object");
|
|
6548
|
+
}
|
|
6549
|
+
const out = {};
|
|
6550
|
+
if (config.overFetchFactor !== void 0) {
|
|
6551
|
+
assertFiniteNumber2(config.overFetchFactor, "retrieval.overFetchFactor");
|
|
6552
|
+
assertNumberMin(config.overFetchFactor, "retrieval.overFetchFactor", 0);
|
|
6553
|
+
if (config.overFetchFactor <= 0) {
|
|
6554
|
+
throw new ConfigurationError("retrieval.overFetchFactor must be > 0");
|
|
6555
|
+
}
|
|
6556
|
+
out.overFetchFactor = config.overFetchFactor;
|
|
6557
|
+
}
|
|
6558
|
+
if (config.hybrid !== void 0) {
|
|
6559
|
+
if (config.hybrid === null || typeof config.hybrid !== "object") {
|
|
6560
|
+
throw new ConfigurationError("retrieval.hybrid must be an object");
|
|
6561
|
+
}
|
|
6562
|
+
const hybrid = config.hybrid;
|
|
6563
|
+
const outHybrid = {};
|
|
6564
|
+
if (hybrid.semanticWeight !== void 0) {
|
|
6565
|
+
assertFiniteNumber2(hybrid.semanticWeight, "retrieval.hybrid.semanticWeight");
|
|
6566
|
+
if (hybrid.semanticWeight < 0) {
|
|
6567
|
+
throw new ConfigurationError(
|
|
6568
|
+
"retrieval.hybrid.semanticWeight must be >= 0"
|
|
6569
|
+
);
|
|
6570
|
+
}
|
|
6571
|
+
outHybrid.semanticWeight = hybrid.semanticWeight;
|
|
6572
|
+
}
|
|
6573
|
+
if (hybrid.keywordWeight !== void 0) {
|
|
6574
|
+
assertFiniteNumber2(hybrid.keywordWeight, "retrieval.hybrid.keywordWeight");
|
|
6575
|
+
if (hybrid.keywordWeight < 0) {
|
|
6576
|
+
throw new ConfigurationError(
|
|
6577
|
+
"retrieval.hybrid.keywordWeight must be >= 0"
|
|
6578
|
+
);
|
|
6579
|
+
}
|
|
6580
|
+
outHybrid.keywordWeight = hybrid.keywordWeight;
|
|
6581
|
+
}
|
|
6582
|
+
if (Object.keys(outHybrid).length > 0) {
|
|
6583
|
+
out.hybrid = outHybrid;
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
if (config.mmr !== void 0) {
|
|
6587
|
+
if (config.mmr === null || typeof config.mmr !== "object") {
|
|
6588
|
+
throw new ConfigurationError("retrieval.mmr must be an object");
|
|
6589
|
+
}
|
|
6590
|
+
const mmr = config.mmr;
|
|
6591
|
+
const outMmr = {};
|
|
6592
|
+
if (mmr.lambda !== void 0) {
|
|
6593
|
+
assertFiniteNumber2(mmr.lambda, "retrieval.mmr.lambda");
|
|
6594
|
+
assertNumberInRange(mmr.lambda, "retrieval.mmr.lambda", 0, 1);
|
|
6595
|
+
outMmr.lambda = mmr.lambda;
|
|
6596
|
+
}
|
|
6597
|
+
if (Object.keys(outMmr).length > 0) {
|
|
6598
|
+
out.mmr = outMmr;
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
return out;
|
|
6602
|
+
}
|
|
6603
|
+
function validateMemoryDedupeConfig(config) {
|
|
6604
|
+
if (config === null || typeof config !== "object") {
|
|
6605
|
+
throw new ConfigurationError("memory.dedupe must be an object");
|
|
6606
|
+
}
|
|
6607
|
+
const out = {};
|
|
6608
|
+
if (config.enabled !== void 0) {
|
|
6609
|
+
if (typeof config.enabled !== "boolean") {
|
|
6610
|
+
throw new ConfigurationError("memory.dedupe.enabled must be a boolean");
|
|
6611
|
+
}
|
|
6612
|
+
out.enabled = config.enabled;
|
|
6613
|
+
}
|
|
6614
|
+
if (config.strategy !== void 0) {
|
|
6615
|
+
const allowed = /* @__PURE__ */ new Set(["exact", "near", "exact-or-near"]);
|
|
6616
|
+
if (!allowed.has(config.strategy)) {
|
|
6617
|
+
throw new ConfigurationError(
|
|
6618
|
+
`memory.dedupe.strategy must be one of ${Array.from(allowed).join(", ")}`
|
|
6619
|
+
);
|
|
6620
|
+
}
|
|
6621
|
+
out.strategy = config.strategy;
|
|
6622
|
+
}
|
|
6623
|
+
if (config.nearThreshold !== void 0) {
|
|
6624
|
+
assertFiniteNumber2(config.nearThreshold, "memory.dedupe.nearThreshold");
|
|
6625
|
+
assertNumberInRange(
|
|
6626
|
+
config.nearThreshold,
|
|
6627
|
+
"memory.dedupe.nearThreshold",
|
|
6628
|
+
0,
|
|
6629
|
+
1
|
|
6630
|
+
);
|
|
6631
|
+
out.nearThreshold = config.nearThreshold;
|
|
6632
|
+
}
|
|
6633
|
+
if (config.nearCandidateLimit !== void 0) {
|
|
6634
|
+
assertFiniteNumber2(
|
|
6635
|
+
config.nearCandidateLimit,
|
|
6636
|
+
"memory.dedupe.nearCandidateLimit"
|
|
6637
|
+
);
|
|
6638
|
+
assertNumberMin(
|
|
6639
|
+
config.nearCandidateLimit,
|
|
6640
|
+
"memory.dedupe.nearCandidateLimit",
|
|
6641
|
+
1
|
|
6642
|
+
);
|
|
6643
|
+
out.nearCandidateLimit = config.nearCandidateLimit;
|
|
6644
|
+
}
|
|
6645
|
+
return out;
|
|
6646
|
+
}
|
|
6647
|
+
function validateEmbeddingCacheConfig(config) {
|
|
6648
|
+
if (config === null || typeof config !== "object") {
|
|
6649
|
+
throw new ConfigurationError("embeddingCache must be an object");
|
|
6650
|
+
}
|
|
6651
|
+
const out = {};
|
|
6652
|
+
if (config.enabled !== void 0) {
|
|
6653
|
+
if (typeof config.enabled !== "boolean") {
|
|
6654
|
+
throw new ConfigurationError("embeddingCache.enabled must be a boolean");
|
|
6655
|
+
}
|
|
6656
|
+
out.enabled = config.enabled;
|
|
6657
|
+
}
|
|
6658
|
+
if (config.ttlMs !== void 0) {
|
|
6659
|
+
assertFiniteNumber2(config.ttlMs, "embeddingCache.ttlMs");
|
|
6660
|
+
if (config.ttlMs < 0) {
|
|
6661
|
+
throw new ConfigurationError("embeddingCache.ttlMs must be >= 0");
|
|
6662
|
+
}
|
|
6663
|
+
out.ttlMs = config.ttlMs;
|
|
6664
|
+
}
|
|
6665
|
+
if (config.maxEntries !== void 0) {
|
|
6666
|
+
assertFiniteNumber2(config.maxEntries, "embeddingCache.maxEntries");
|
|
6667
|
+
if (config.maxEntries < 0) {
|
|
6668
|
+
throw new ConfigurationError("embeddingCache.maxEntries must be >= 0");
|
|
6669
|
+
}
|
|
6670
|
+
out.maxEntries = config.maxEntries;
|
|
6671
|
+
}
|
|
6672
|
+
return out;
|
|
6673
|
+
}
|
|
6263
6674
|
function normalizeDatabaseConfig(config) {
|
|
6264
6675
|
const provider = config.provider;
|
|
6265
6676
|
if (provider !== "sqlite" && provider !== "postgres") {
|
|
@@ -6369,11 +6780,22 @@ function validateWolbargOptions(options) {
|
|
|
6369
6780
|
throw new ConfigurationError("Wolbarg options must be an object");
|
|
6370
6781
|
}
|
|
6371
6782
|
assertNonEmpty(options.organization, "organization");
|
|
6783
|
+
const checkpointDirectory = options.checkpointDirectory !== void 0 ? (() => {
|
|
6784
|
+
assertNonEmpty(options.checkpointDirectory, "checkpointDirectory");
|
|
6785
|
+
return options.checkpointDirectory.trim();
|
|
6786
|
+
})() : void 0;
|
|
6372
6787
|
const storageInput = resolveStorageInput(options);
|
|
6373
6788
|
let storage = storageInput;
|
|
6374
6789
|
if (!isStorageProvider(storageInput)) {
|
|
6375
6790
|
storage = normalizeDatabaseConfig(storageInput);
|
|
6376
6791
|
}
|
|
6792
|
+
if (!isStorageProvider(storageInput) && storageInput.provider === "postgres" && storageInput.maxPoolSize !== void 0) {
|
|
6793
|
+
assertFiniteNumber2(
|
|
6794
|
+
storageInput.maxPoolSize,
|
|
6795
|
+
"database.maxPoolSize"
|
|
6796
|
+
);
|
|
6797
|
+
assertNumberMin(storageInput.maxPoolSize, "database.maxPoolSize", 1);
|
|
6798
|
+
}
|
|
6377
6799
|
if (!options.embedding) {
|
|
6378
6800
|
throw new ConfigurationError("embedding is required");
|
|
6379
6801
|
}
|
|
@@ -6391,13 +6813,20 @@ function validateWolbargOptions(options) {
|
|
|
6391
6813
|
if (graph !== void 0) {
|
|
6392
6814
|
graph = resolveGraphInput(graph);
|
|
6393
6815
|
}
|
|
6816
|
+
const retrieval = options.retrieval !== void 0 ? validateRetrievalConfig(options.retrieval) : void 0;
|
|
6817
|
+
const embeddingCache = options.embeddingCache !== void 0 ? validateEmbeddingCacheConfig(options.embeddingCache) : void 0;
|
|
6818
|
+
const memory = options.memory !== void 0 && options.memory.dedupe !== void 0 ? { ...options.memory, dedupe: validateMemoryDedupeConfig(options.memory.dedupe) } : options.memory;
|
|
6394
6819
|
return {
|
|
6395
6820
|
...options,
|
|
6396
6821
|
organization: options.organization.trim(),
|
|
6397
6822
|
storage,
|
|
6398
6823
|
database: void 0,
|
|
6399
6824
|
...telemetry ? { telemetry } : {},
|
|
6400
|
-
...graph !== void 0 ? { graph } : {}
|
|
6825
|
+
...graph !== void 0 ? { graph } : {},
|
|
6826
|
+
...retrieval !== void 0 ? { retrieval } : {},
|
|
6827
|
+
...embeddingCache !== void 0 ? { embeddingCache } : {},
|
|
6828
|
+
...checkpointDirectory !== void 0 ? { checkpointDirectory } : {},
|
|
6829
|
+
...memory !== void 0 ? { memory } : {}
|
|
6401
6830
|
};
|
|
6402
6831
|
}
|
|
6403
6832
|
function resolveGraphInput(input) {
|
|
@@ -6456,59 +6885,6 @@ function resolveGraphInput(input) {
|
|
|
6456
6885
|
);
|
|
6457
6886
|
}
|
|
6458
6887
|
|
|
6459
|
-
// src/telemetry/logger.ts
|
|
6460
|
-
var LEVEL_ORDER = {
|
|
6461
|
-
off: 100,
|
|
6462
|
-
error: 50,
|
|
6463
|
-
warn: 40,
|
|
6464
|
-
info: 30,
|
|
6465
|
-
debug: 20,
|
|
6466
|
-
trace: 10
|
|
6467
|
-
};
|
|
6468
|
-
var WolbargLogger = class {
|
|
6469
|
-
constructor(level = "info") {
|
|
6470
|
-
this.level = level;
|
|
6471
|
-
}
|
|
6472
|
-
level;
|
|
6473
|
-
setLevel(level) {
|
|
6474
|
-
this.level = level;
|
|
6475
|
-
}
|
|
6476
|
-
getLevel() {
|
|
6477
|
-
return this.level;
|
|
6478
|
-
}
|
|
6479
|
-
error(message, extra) {
|
|
6480
|
-
this.write("error", message, extra);
|
|
6481
|
-
}
|
|
6482
|
-
warn(message, extra) {
|
|
6483
|
-
this.write("warn", message, extra);
|
|
6484
|
-
}
|
|
6485
|
-
info(message, extra) {
|
|
6486
|
-
this.write("info", message, extra);
|
|
6487
|
-
}
|
|
6488
|
-
debug(message, extra) {
|
|
6489
|
-
this.write("debug", message, extra);
|
|
6490
|
-
}
|
|
6491
|
-
trace(message, extra) {
|
|
6492
|
-
this.write("trace", message, extra);
|
|
6493
|
-
}
|
|
6494
|
-
write(level, message, extra) {
|
|
6495
|
-
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
|
|
6496
|
-
return;
|
|
6497
|
-
}
|
|
6498
|
-
if (this.level === "off") {
|
|
6499
|
-
return;
|
|
6500
|
-
}
|
|
6501
|
-
const line = `[wolbarg:${level}] ${message}`;
|
|
6502
|
-
if (level === "error") {
|
|
6503
|
-
console.error(line, extra ?? "");
|
|
6504
|
-
} else if (level === "warn") {
|
|
6505
|
-
console.warn(line, extra ?? "");
|
|
6506
|
-
} else {
|
|
6507
|
-
console.log(line, extra ?? "");
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
};
|
|
6511
|
-
|
|
6512
6888
|
// src/telemetry/trace.ts
|
|
6513
6889
|
function createSessionId() {
|
|
6514
6890
|
return createId();
|
|
@@ -6593,14 +6969,14 @@ var TelemetryEmitter = class {
|
|
|
6593
6969
|
const totalMs = performance.now() - startedAt;
|
|
6594
6970
|
const errMessage = error instanceof Error ? error.message : error ? String(error) : fields?.error ?? null;
|
|
6595
6971
|
const errStack = error instanceof Error ? error.stack ?? null : fields?.errorStack ?? null;
|
|
6972
|
+
if (!self.config.enabled) {
|
|
6973
|
+
return;
|
|
6974
|
+
}
|
|
6596
6975
|
if (status === "error") {
|
|
6597
6976
|
self.logger.error(`${operation} failed: ${errMessage ?? "unknown"}`);
|
|
6598
6977
|
} else {
|
|
6599
6978
|
self.logger.debug(`${operation} completed in ${totalMs.toFixed(2)}ms`);
|
|
6600
6979
|
}
|
|
6601
|
-
if (!self.config.enabled) {
|
|
6602
|
-
return;
|
|
6603
|
-
}
|
|
6604
6980
|
const event = {
|
|
6605
6981
|
id: createId(),
|
|
6606
6982
|
timestamp: nowIso(),
|
|
@@ -6902,8 +7278,10 @@ var SqliteEventDatabase = class {
|
|
|
6902
7278
|
}
|
|
6903
7279
|
}
|
|
6904
7280
|
if (options.memoryId) {
|
|
6905
|
-
clauses.push(
|
|
6906
|
-
|
|
7281
|
+
clauses.push(
|
|
7282
|
+
`EXISTS (SELECT 1 FROM json_each(memory_ids_json) WHERE json_each.value = ?)`
|
|
7283
|
+
);
|
|
7284
|
+
params.push(options.memoryId);
|
|
6907
7285
|
}
|
|
6908
7286
|
if (options.queryText) {
|
|
6909
7287
|
clauses.push(`query LIKE ?`);
|
|
@@ -7080,6 +7458,7 @@ function describe2(error) {
|
|
|
7080
7458
|
}
|
|
7081
7459
|
|
|
7082
7460
|
// src/providers/sqlite/sqliteTelemetryProvider.ts
|
|
7461
|
+
var MAX_QUEUE_SIZE = 1e4;
|
|
7083
7462
|
var SqliteTelemetryProvider = class {
|
|
7084
7463
|
name = "sqlite";
|
|
7085
7464
|
db;
|
|
@@ -7087,6 +7466,8 @@ var SqliteTelemetryProvider = class {
|
|
|
7087
7466
|
flushing = null;
|
|
7088
7467
|
closed = false;
|
|
7089
7468
|
openPromise = null;
|
|
7469
|
+
warnedQueueDrop = false;
|
|
7470
|
+
warnedFlushFailure = false;
|
|
7090
7471
|
constructor(options) {
|
|
7091
7472
|
this.db = new SqliteEventDatabase({ url: options.url });
|
|
7092
7473
|
}
|
|
@@ -7107,6 +7488,15 @@ var SqliteTelemetryProvider = class {
|
|
|
7107
7488
|
if (this.closed) {
|
|
7108
7489
|
return;
|
|
7109
7490
|
}
|
|
7491
|
+
if (this.queue.length >= MAX_QUEUE_SIZE) {
|
|
7492
|
+
this.queue.shift();
|
|
7493
|
+
if (!this.warnedQueueDrop) {
|
|
7494
|
+
this.warnedQueueDrop = true;
|
|
7495
|
+
console.warn(
|
|
7496
|
+
`[wolbarg telemetry] event queue capped at ${MAX_QUEUE_SIZE}; dropping oldest events`
|
|
7497
|
+
);
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7110
7500
|
this.queue.push(event);
|
|
7111
7501
|
void this.scheduleFlush();
|
|
7112
7502
|
}
|
|
@@ -7149,7 +7539,13 @@ var SqliteTelemetryProvider = class {
|
|
|
7149
7539
|
await this.db.insertEvent(event);
|
|
7150
7540
|
}
|
|
7151
7541
|
}
|
|
7152
|
-
} catch {
|
|
7542
|
+
} catch (error) {
|
|
7543
|
+
if (!this.warnedFlushFailure) {
|
|
7544
|
+
this.warnedFlushFailure = true;
|
|
7545
|
+
console.warn(
|
|
7546
|
+
`[wolbarg telemetry] flush failed; dropping batch: ${error instanceof Error ? error.message : String(error)}`
|
|
7547
|
+
);
|
|
7548
|
+
}
|
|
7153
7549
|
}
|
|
7154
7550
|
}
|
|
7155
7551
|
};
|
|
@@ -7192,8 +7588,20 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7192
7588
|
);
|
|
7193
7589
|
}
|
|
7194
7590
|
const snapshotPath = this.snapshotPath(name);
|
|
7195
|
-
|
|
7196
|
-
|
|
7591
|
+
const tmpSnapshotPath = `${snapshotPath}.tmp-${Date.now()}`;
|
|
7592
|
+
try {
|
|
7593
|
+
await safeSqliteBackup(resolvedSource, tmpSnapshotPath);
|
|
7594
|
+
} catch (error) {
|
|
7595
|
+
if (fs6.existsSync(tmpSnapshotPath)) {
|
|
7596
|
+
fs6.rmSync(tmpSnapshotPath, { force: true });
|
|
7597
|
+
}
|
|
7598
|
+
throw error;
|
|
7599
|
+
}
|
|
7600
|
+
const stats = fs6.statSync(tmpSnapshotPath);
|
|
7601
|
+
if (fs6.existsSync(snapshotPath)) {
|
|
7602
|
+
fs6.rmSync(snapshotPath, { force: true });
|
|
7603
|
+
}
|
|
7604
|
+
fs6.renameSync(tmpSnapshotPath, snapshotPath);
|
|
7197
7605
|
const meta2 = {
|
|
7198
7606
|
name,
|
|
7199
7607
|
description: options?.description ?? null,
|
|
@@ -7204,7 +7612,9 @@ Ensure Wolbarg has been initialized and the database path is correct.`
|
|
|
7204
7612
|
sourcePath: resolvedSource,
|
|
7205
7613
|
sizeBytes: stats.size
|
|
7206
7614
|
};
|
|
7207
|
-
|
|
7615
|
+
const tmpMetaPath = `${metaPath}.tmp-${Date.now()}`;
|
|
7616
|
+
fs6.writeFileSync(tmpMetaPath, JSON.stringify(meta2, null, 2), "utf8");
|
|
7617
|
+
fs6.renameSync(tmpMetaPath, metaPath);
|
|
7208
7618
|
return meta2;
|
|
7209
7619
|
}
|
|
7210
7620
|
async rollback(name, targetPath) {
|
|
@@ -7468,6 +7878,9 @@ var SqliteSubscribeEmitter = class {
|
|
|
7468
7878
|
};
|
|
7469
7879
|
|
|
7470
7880
|
// src/core/wolbarg.ts
|
|
7881
|
+
var warnLogger3 = new WolbargLogger("warn");
|
|
7882
|
+
var warnedHybridNoKeyword = false;
|
|
7883
|
+
var warnedRerankNoProvider = false;
|
|
7471
7884
|
var Wolbarg = class {
|
|
7472
7885
|
initialized = false;
|
|
7473
7886
|
booting = null;
|
|
@@ -7573,6 +7986,7 @@ var Wolbarg = class {
|
|
|
7573
7986
|
this.storage = createStorageProvider(validated.database);
|
|
7574
7987
|
this.memoryDbPath = resolveDatabaseUrl(validated.database);
|
|
7575
7988
|
this.embedding = createEmbeddingProvider(validated.embedding);
|
|
7989
|
+
this.rawEmbedding = this.embedding;
|
|
7576
7990
|
if (validated.llm) {
|
|
7577
7991
|
this.llm = createLlmProvider(validated.llm);
|
|
7578
7992
|
this.compression = createCompressionProvider(this.llm);
|
|
@@ -7894,7 +8308,7 @@ var Wolbarg = class {
|
|
|
7894
8308
|
* Update an existing memory by id (re-embeds when content changes).
|
|
7895
8309
|
*/
|
|
7896
8310
|
async update(options) {
|
|
7897
|
-
const trace = this.telemetry.start("
|
|
8311
|
+
const trace = this.telemetry.start("update");
|
|
7898
8312
|
try {
|
|
7899
8313
|
const { storage, embedding, organization } = await this.requireReady();
|
|
7900
8314
|
assertNonEmptyString(options.id, "id");
|
|
@@ -8420,17 +8834,37 @@ var Wolbarg = class {
|
|
|
8420
8834
|
}
|
|
8421
8835
|
}
|
|
8422
8836
|
}
|
|
8837
|
+
if (hybridWeights && keywordSignal === "disabled" && !warnedHybridNoKeyword) {
|
|
8838
|
+
warnedHybridNoKeyword = true;
|
|
8839
|
+
warnLogger3.warn(
|
|
8840
|
+
"hybrid search requested but no keyword channel is available; using semantic-only results."
|
|
8841
|
+
);
|
|
8842
|
+
}
|
|
8423
8843
|
const tRank = performance.now();
|
|
8424
|
-
let results = [...byId.values()].sort(
|
|
8425
|
-
|
|
8426
|
-
|
|
8844
|
+
let results = [...byId.values()].sort((a, b) => {
|
|
8845
|
+
const diff = b.similarity - a.similarity;
|
|
8846
|
+
if (diff !== 0) return diff;
|
|
8847
|
+
return a.id.localeCompare(b.id);
|
|
8848
|
+
});
|
|
8427
8849
|
const mmrLambda = resolveMmr(options.mmr) ?? resolveMmr(
|
|
8428
8850
|
this.retrievalConfig.mmr ? { lambda: this.retrievalConfig.mmr.lambda } : void 0
|
|
8429
8851
|
);
|
|
8430
8852
|
let rankingReason = "cosine similarity";
|
|
8853
|
+
let mmrApplied = false;
|
|
8431
8854
|
if (mmrLambda !== null) {
|
|
8432
|
-
|
|
8433
|
-
|
|
8855
|
+
mmrApplied = results.length > topK;
|
|
8856
|
+
if (mmrApplied) {
|
|
8857
|
+
results = applyMmr(results, topK, mmrLambda);
|
|
8858
|
+
rankingReason = `MMR(lambda=${mmrLambda})`;
|
|
8859
|
+
} else {
|
|
8860
|
+
rankingReason = `MMR(lambda=${mmrLambda})(skipped:insufficient_candidates)`;
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
if (options.rerank === true && !this.reranker && !warnedRerankNoProvider) {
|
|
8864
|
+
warnedRerankNoProvider = true;
|
|
8865
|
+
warnLogger3.warn(
|
|
8866
|
+
"rerank is enabled but no reranker provider is configured; using identity order."
|
|
8867
|
+
);
|
|
8434
8868
|
}
|
|
8435
8869
|
if (options.rerank && this.reranker) {
|
|
8436
8870
|
const reranked = await this.reranker.rerank(
|
|
@@ -8465,14 +8899,16 @@ var Wolbarg = class {
|
|
|
8465
8899
|
agentId: options.filter?.agent ?? commonAgent(serialized.map((result) => result.agent)),
|
|
8466
8900
|
tags: commonTags(serialized.map((result) => result.metadata))
|
|
8467
8901
|
};
|
|
8468
|
-
if (
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8902
|
+
if (options.includeGraph === true && this.graph) {
|
|
8903
|
+
const tGraph = performance.now();
|
|
8904
|
+
await Promise.all(
|
|
8905
|
+
serialized.map(async (hit) => {
|
|
8472
8906
|
hit.related = await this.hydrateRelated(hit.id);
|
|
8473
|
-
}
|
|
8474
|
-
|
|
8475
|
-
|
|
8907
|
+
})
|
|
8908
|
+
);
|
|
8909
|
+
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8910
|
+
}
|
|
8911
|
+
if (!explain) {
|
|
8476
8912
|
trace.success(telemetryFields);
|
|
8477
8913
|
return serialized;
|
|
8478
8914
|
}
|
|
@@ -8501,7 +8937,7 @@ var Wolbarg = class {
|
|
|
8501
8937
|
semantic: "enabled",
|
|
8502
8938
|
keyword: keywordSignal,
|
|
8503
8939
|
reranker: options.rerank === true && this.reranker ? "enabled" : "disabled",
|
|
8504
|
-
mmr: mmrLambda === null ? "disabled" : "enabled",
|
|
8940
|
+
mmr: mmrLambda === null ? "disabled" : mmrApplied ? "enabled" : "disabled",
|
|
8505
8941
|
recency: "disabled"
|
|
8506
8942
|
},
|
|
8507
8943
|
results: explanations.map((hit) => ({
|
|
@@ -8610,31 +9046,34 @@ var Wolbarg = class {
|
|
|
8610
9046
|
const timestamp = nowIso();
|
|
8611
9047
|
const result = await this.withWriteLock(async () => {
|
|
8612
9048
|
const tWrite = performance.now();
|
|
8613
|
-
const
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
9049
|
+
const txResult = await storage.withTransaction(async () => {
|
|
9050
|
+
const summaryRow = await storage.insertMemory({
|
|
9051
|
+
id: summaryId,
|
|
9052
|
+
organization,
|
|
9053
|
+
agent: options.agent.trim(),
|
|
9054
|
+
contentText: summaryText,
|
|
9055
|
+
metadata: {
|
|
9056
|
+
compressed: true,
|
|
9057
|
+
sourceCount: records.length,
|
|
9058
|
+
sourceIds: records.map((r) => r.id)
|
|
9059
|
+
},
|
|
9060
|
+
embedding: vector,
|
|
9061
|
+
createdAt: timestamp,
|
|
9062
|
+
updatedAt: timestamp
|
|
9063
|
+
});
|
|
9064
|
+
const archivedIds = await storage.archiveMemories(
|
|
9065
|
+
records.map((r) => r.id),
|
|
9066
|
+
organization,
|
|
9067
|
+
summaryId,
|
|
9068
|
+
timestamp
|
|
9069
|
+
);
|
|
9070
|
+
return {
|
|
9071
|
+
summary: toMemoryRecord(summaryRow),
|
|
9072
|
+
archivedIds
|
|
9073
|
+
};
|
|
8626
9074
|
});
|
|
8627
|
-
const archivedIds = await storage.archiveMemories(
|
|
8628
|
-
records.map((r) => r.id),
|
|
8629
|
-
organization,
|
|
8630
|
-
summaryId,
|
|
8631
|
-
timestamp
|
|
8632
|
-
);
|
|
8633
9075
|
trace.mark("databaseWriteMs", performance.now() - tWrite);
|
|
8634
|
-
return
|
|
8635
|
-
summary: toMemoryRecord(summaryRow),
|
|
8636
|
-
archivedIds
|
|
8637
|
-
};
|
|
9076
|
+
return txResult;
|
|
8638
9077
|
});
|
|
8639
9078
|
trace.success({
|
|
8640
9079
|
provider: storage.name,
|
|
@@ -8866,8 +9305,23 @@ var Wolbarg = class {
|
|
|
8866
9305
|
await this.requireReady();
|
|
8867
9306
|
const graph = this.requireGraph("getRelated");
|
|
8868
9307
|
assertNonEmptyString(memoryId, "memoryId");
|
|
9308
|
+
let validatedOptions = options;
|
|
9309
|
+
if (options?.depth !== void 0) {
|
|
9310
|
+
if (!Number.isFinite(options.depth)) {
|
|
9311
|
+
throw new ConfigurationError("graph.depth must be a finite number");
|
|
9312
|
+
}
|
|
9313
|
+
if (options.depth < 1) {
|
|
9314
|
+
throw new ConfigurationError("graph.depth must be >= 1");
|
|
9315
|
+
}
|
|
9316
|
+
if (options.depth > 16) {
|
|
9317
|
+
validatedOptions = { ...options, depth: 16 };
|
|
9318
|
+
}
|
|
9319
|
+
}
|
|
8869
9320
|
const tGraph = performance.now();
|
|
8870
|
-
const related = await this.hydrateRelated(
|
|
9321
|
+
const related = await this.hydrateRelated(
|
|
9322
|
+
memoryId.trim(),
|
|
9323
|
+
validatedOptions
|
|
9324
|
+
);
|
|
8871
9325
|
trace.mark("databaseReadMs", performance.now() - tGraph);
|
|
8872
9326
|
trace.success({
|
|
8873
9327
|
provider: graph.name,
|
|
@@ -9036,7 +9490,15 @@ var Wolbarg = class {
|
|
|
9036
9490
|
this.assertGraphCheckpointSupported("checkpoint");
|
|
9037
9491
|
const tCheckpoint = performance.now();
|
|
9038
9492
|
const meta2 = await provider.checkpoint(name, source, options);
|
|
9039
|
-
|
|
9493
|
+
try {
|
|
9494
|
+
await this.snapshotGraphAlongside(meta2.snapshotPath, "checkpoint");
|
|
9495
|
+
} catch (error) {
|
|
9496
|
+
const sidecar = this.graphSnapshotDir(meta2.snapshotPath);
|
|
9497
|
+
if (fs6.existsSync(sidecar)) {
|
|
9498
|
+
fs6.rmSync(sidecar, { recursive: true, force: true });
|
|
9499
|
+
}
|
|
9500
|
+
throw error;
|
|
9501
|
+
}
|
|
9040
9502
|
trace.mark("databaseWriteMs", performance.now() - tCheckpoint);
|
|
9041
9503
|
trace.success({
|
|
9042
9504
|
provider: provider.name,
|
|
@@ -9100,8 +9562,15 @@ var Wolbarg = class {
|
|
|
9100
9562
|
try {
|
|
9101
9563
|
await this.requireReady();
|
|
9102
9564
|
const provider = this.requireCheckpointProvider();
|
|
9565
|
+
const existing = await provider.getCheckpoint(name);
|
|
9103
9566
|
const tDelete = performance.now();
|
|
9104
9567
|
const removed = await provider.deleteCheckpoint(name);
|
|
9568
|
+
if (removed && existing) {
|
|
9569
|
+
const sidecar = this.graphSnapshotDir(existing.snapshotPath);
|
|
9570
|
+
if (fs6.existsSync(sidecar)) {
|
|
9571
|
+
fs6.rmSync(sidecar, { recursive: true, force: true });
|
|
9572
|
+
}
|
|
9573
|
+
}
|
|
9105
9574
|
trace.mark("databaseWriteMs", performance.now() - tDelete);
|
|
9106
9575
|
trace.success({
|
|
9107
9576
|
provider: provider.name,
|
|
@@ -9163,7 +9632,9 @@ var Wolbarg = class {
|
|
|
9163
9632
|
const result = await this.transfer.exportTo(
|
|
9164
9633
|
exportPath,
|
|
9165
9634
|
source,
|
|
9166
|
-
this.organization ?? void 0
|
|
9635
|
+
this.organization ?? void 0,
|
|
9636
|
+
this.embedding?.model,
|
|
9637
|
+
this.embeddingDimensions ?? void 0
|
|
9167
9638
|
);
|
|
9168
9639
|
await this.snapshotGraphAlongside(result.path, "export");
|
|
9169
9640
|
trace.success({
|
|
@@ -9186,16 +9657,21 @@ var Wolbarg = class {
|
|
|
9186
9657
|
let storageClosed = false;
|
|
9187
9658
|
try {
|
|
9188
9659
|
await this.requireReady();
|
|
9660
|
+
this.assertGraphCheckpointSupported("import");
|
|
9189
9661
|
const target = this.requireMemoryDbPath();
|
|
9190
9662
|
if (this.storage) {
|
|
9191
9663
|
await this.storage.close();
|
|
9192
9664
|
storageClosed = true;
|
|
9193
9665
|
}
|
|
9194
9666
|
const graphClosed = await this.closeGraphForSnapshot();
|
|
9195
|
-
this.assertGraphCheckpointSupported("import");
|
|
9196
9667
|
const result = await this.transfer.importFrom(
|
|
9197
9668
|
exportPath,
|
|
9198
|
-
target
|
|
9669
|
+
target,
|
|
9670
|
+
{
|
|
9671
|
+
organization: this.organization ?? void 0,
|
|
9672
|
+
embeddingModel: this.embedding?.model,
|
|
9673
|
+
embeddingDimensions: this.embeddingDimensions ?? void 0
|
|
9674
|
+
}
|
|
9199
9675
|
);
|
|
9200
9676
|
await this.restoreGraphAlongside(result.path, "import");
|
|
9201
9677
|
if (graphClosed) {
|
|
@@ -9390,18 +9866,26 @@ var Wolbarg = class {
|
|
|
9390
9866
|
const graph = this.requireGraph("getRelated");
|
|
9391
9867
|
const related = await graph.getRelated(memoryId, options);
|
|
9392
9868
|
const { storage, organization } = await this.requireReady();
|
|
9869
|
+
const rows = await Promise.all(
|
|
9870
|
+
related.map((stub) => storage.getMemoryById(stub.id, organization))
|
|
9871
|
+
);
|
|
9393
9872
|
const out = [];
|
|
9394
|
-
for (
|
|
9395
|
-
const
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
} else {
|
|
9399
|
-
out.push(stub);
|
|
9400
|
-
}
|
|
9873
|
+
for (let i = 0; i < related.length; i += 1) {
|
|
9874
|
+
const stub = related[i];
|
|
9875
|
+
const row = rows[i];
|
|
9876
|
+
out.push(row ? toMemoryRecord(row) : stub);
|
|
9401
9877
|
}
|
|
9402
9878
|
return out;
|
|
9403
9879
|
}
|
|
9404
9880
|
requireMemoryDbPath() {
|
|
9881
|
+
if (this.storage && !(this.storage instanceof SqliteStorageProvider)) {
|
|
9882
|
+
throw new ConfigurationError(
|
|
9883
|
+
"This operation requires a file-backed SQLite database (checkpoints / export-import are not supported for PostgreSQL).",
|
|
9884
|
+
{
|
|
9885
|
+
suggestion: 'Use database: { provider: "sqlite", url: "./memory.db" }'
|
|
9886
|
+
}
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9405
9889
|
if (!this.memoryDbPath || this.memoryDbPath === ":memory:") {
|
|
9406
9890
|
throw new ConfigurationError(
|
|
9407
9891
|
"This operation requires a file-backed SQLite memory database (not :memory:).",
|
|
@@ -9630,7 +10114,14 @@ var HttpRerankerProvider = class {
|
|
|
9630
10114
|
}));
|
|
9631
10115
|
}
|
|
9632
10116
|
const parsed = this.options.parseResults(body, documents);
|
|
9633
|
-
|
|
10117
|
+
const hits = parsed.slice(0, topK);
|
|
10118
|
+
if (hits.length === 0) {
|
|
10119
|
+
return documents.slice(0, topK).map((d, i) => ({
|
|
10120
|
+
id: d.id,
|
|
10121
|
+
score: 1 - i / Math.max(documents.length, 1)
|
|
10122
|
+
}));
|
|
10123
|
+
}
|
|
10124
|
+
return hits;
|
|
9634
10125
|
} catch {
|
|
9635
10126
|
return documents.slice(0, topK).map((d, i) => ({
|
|
9636
10127
|
id: d.id,
|
|
@@ -9815,21 +10306,31 @@ function tesseract() {
|
|
|
9815
10306
|
return {
|
|
9816
10307
|
name: "tesseract",
|
|
9817
10308
|
async recognize(image) {
|
|
10309
|
+
let mod;
|
|
9818
10310
|
try {
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
10311
|
+
mod = await import('tesseract.js');
|
|
10312
|
+
} catch (error) {
|
|
10313
|
+
throw new ConfigurationError(
|
|
10314
|
+
'OCR provider "tesseract" requires the optional peer package "tesseract.js". Install it with: npm install tesseract.js',
|
|
10315
|
+
{
|
|
10316
|
+
cause: error instanceof Error ? error : void 0,
|
|
10317
|
+
suggestion: "npm install tesseract.js",
|
|
10318
|
+
operation: "recognize"
|
|
10319
|
+
}
|
|
10320
|
+
);
|
|
10321
|
+
}
|
|
10322
|
+
const createWorker = mod.createWorker ?? mod.default?.createWorker;
|
|
10323
|
+
if (!createWorker) {
|
|
10324
|
+
throw new ConfigurationError(
|
|
10325
|
+
'OCR provider "tesseract" could not find tesseract.js.createWorker. Ensure your tesseract.js version is compatible.'
|
|
10326
|
+
);
|
|
10327
|
+
}
|
|
10328
|
+
const worker = await createWorker("eng");
|
|
10329
|
+
try {
|
|
10330
|
+
const result = await worker.recognize(image);
|
|
10331
|
+
return { text: result.data.text.trim() };
|
|
10332
|
+
} finally {
|
|
10333
|
+
await worker.terminate();
|
|
9833
10334
|
}
|
|
9834
10335
|
}
|
|
9835
10336
|
};
|