@equationalapplications/core-llm-wiki 4.19.0 → 4.21.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/README.md +59 -0
- package/dist/{chunk-3GMXRLKU.mjs → chunk-UYLYN5N4.mjs} +247 -32
- package/dist/chunk-UYLYN5N4.mjs.map +1 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +416 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +169 -14
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DP3lus54.d.mts → testing-i91HR1TG.d.mts} +122 -7
- package/dist/{testing-DP3lus54.d.ts → testing-i91HR1TG.d.ts} +122 -7
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +167 -6
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-3GMXRLKU.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, generateId, BaseRepository, emptyManifest, resolveNodeType, validateInlineEdges, resolveEdgeDefinition, normalizeTitleKey } from './chunk-
|
|
2
|
-
export { HOOK_TIMEOUT_MARKER, PromptService, PrunePartialFailureError, WikiBusyError, configureRandomSource, parseEmbedding } from './chunk-
|
|
1
|
+
import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, generateId, BaseRepository, emptyManifest, resolveNodeType, validateInlineEdges, resolveEdgeDefinition, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-UYLYN5N4.mjs';
|
|
2
|
+
export { HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiTransactionError, configureRandomSource, parseEmbedding } from './chunk-UYLYN5N4.mjs';
|
|
3
3
|
import { appendRelatedSection, buildConceptDocument, buildLogMd, buildEntityIndexMd, buildRootIndexMd, isAllowedOkfPath, parseRootIndexMd, parseEntityIndexMd, parseConcept, splitRelatedSection, extractMarkdownLinks, parseLogMd, parseEventIdComment, appendEventIdComment } from '@equationalapplications/core-okf';
|
|
4
4
|
|
|
5
5
|
// src/db/schema.ts
|
|
@@ -22,7 +22,8 @@ async function setupDatabase(db, prefix) {
|
|
|
22
22
|
deleted_at INTEGER,
|
|
23
23
|
embedding TEXT,
|
|
24
24
|
embedding_blob BLOB,
|
|
25
|
-
okf_type TEXT
|
|
25
|
+
okf_type TEXT,
|
|
26
|
+
ontology_checked_at INTEGER
|
|
26
27
|
);
|
|
27
28
|
|
|
28
29
|
CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
|
|
@@ -104,6 +105,50 @@ async function setupDatabase(db, prefix) {
|
|
|
104
105
|
`);
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
// src/db/serializedAdapter.ts
|
|
109
|
+
function isDriverError(err) {
|
|
110
|
+
return extractSqliteCode(err) !== void 0;
|
|
111
|
+
}
|
|
112
|
+
function withOverrides(target, overrides) {
|
|
113
|
+
return new Proxy(target, {
|
|
114
|
+
get(obj, prop, receiver) {
|
|
115
|
+
if (prop in overrides) return Reflect.get(overrides, prop, receiver);
|
|
116
|
+
const value = Reflect.get(obj, prop, obj);
|
|
117
|
+
return typeof value === "function" ? value.bind(obj) : value;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function guardReentrancy(tx) {
|
|
122
|
+
return withOverrides(tx, {
|
|
123
|
+
withTransactionAsync() {
|
|
124
|
+
throw new Error(
|
|
125
|
+
"Nested withTransactionAsync is not supported: you are already inside a transaction. Pass the current `tx` down instead of opening a new transaction."
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
var DEADLOCK_WARN_MS = 1e4;
|
|
131
|
+
function withSerializedTransactions(db) {
|
|
132
|
+
let queue = Promise.resolve();
|
|
133
|
+
return withOverrides(db, {
|
|
134
|
+
withTransactionAsync(fn) {
|
|
135
|
+
const warn = setTimeout(() => {
|
|
136
|
+
console.warn(
|
|
137
|
+
"[core-llm-wiki] Transaction queued >10s \u2014 possible deadlock. Inside a transaction callback, use the `tx` parameter, never the outer database handle."
|
|
138
|
+
);
|
|
139
|
+
}, DEADLOCK_WARN_MS);
|
|
140
|
+
const run = queue.catch(() => void 0).then(() => {
|
|
141
|
+
clearTimeout(warn);
|
|
142
|
+
return db.withTransactionAsync((tx) => fn(guardReentrancy(tx ?? db)));
|
|
143
|
+
}).catch((e) => {
|
|
144
|
+
throw isDriverError(e) ? new WikiTransactionError("Transaction failed", { cause: e }) : e;
|
|
145
|
+
});
|
|
146
|
+
queue = run;
|
|
147
|
+
return run;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
107
152
|
// src/db/migrations.ts
|
|
108
153
|
var MIGRATIONS = [
|
|
109
154
|
{
|
|
@@ -204,6 +249,20 @@ var MIGRATIONS = [
|
|
|
204
249
|
);
|
|
205
250
|
`);
|
|
206
251
|
}
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
version: 7,
|
|
255
|
+
description: "Add ontology_checked_at to entries for ontology backfill recheck cooldown",
|
|
256
|
+
run: async (db, prefix) => {
|
|
257
|
+
const cols = await db.getAllAsync(
|
|
258
|
+
`PRAGMA table_info(${prefix}entries)`
|
|
259
|
+
);
|
|
260
|
+
if (!cols.some((c) => c.name === "ontology_checked_at")) {
|
|
261
|
+
await db.execAsync(
|
|
262
|
+
`ALTER TABLE ${prefix}entries ADD COLUMN ontology_checked_at INTEGER`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
207
266
|
}
|
|
208
267
|
];
|
|
209
268
|
for (let i = 1; i < MIGRATIONS.length; i++) {
|
|
@@ -952,6 +1011,79 @@ var EntryRepository = class extends BaseRepository {
|
|
|
952
1011
|
);
|
|
953
1012
|
return rows.map(mapRowToFact);
|
|
954
1013
|
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Live untyped facts eligible for ontology backfill, oldest first.
|
|
1016
|
+
* Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
|
|
1017
|
+
*/
|
|
1018
|
+
async findUntypedByEntityId(entityId, limit, recheckCutoff, tx) {
|
|
1019
|
+
const executor = this.getExecutor(tx);
|
|
1020
|
+
const rows = await executor.getAllAsync(
|
|
1021
|
+
`SELECT * FROM ${this.prefix}entries
|
|
1022
|
+
WHERE entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL
|
|
1023
|
+
AND (ontology_checked_at IS NULL OR ontology_checked_at <= ?)
|
|
1024
|
+
ORDER BY updated_at ASC LIMIT ?`,
|
|
1025
|
+
[entityId, recheckCutoff, limit]
|
|
1026
|
+
);
|
|
1027
|
+
return rows.map(mapRowToFact);
|
|
1028
|
+
}
|
|
1029
|
+
/** Counts live untyped facts: eligible (past cooldown) vs deferred (in cooldown). */
|
|
1030
|
+
async countUntypedByEntityId(entityId, recheckCutoff, tx) {
|
|
1031
|
+
const executor = this.getExecutor(tx);
|
|
1032
|
+
const row = await executor.getFirstAsync(
|
|
1033
|
+
`SELECT
|
|
1034
|
+
SUM(CASE WHEN ontology_checked_at IS NULL OR ontology_checked_at <= ? THEN 1 ELSE 0 END) AS eligible,
|
|
1035
|
+
SUM(CASE WHEN ontology_checked_at IS NOT NULL AND ontology_checked_at > ? THEN 1 ELSE 0 END) AS deferred
|
|
1036
|
+
FROM ${this.prefix}entries
|
|
1037
|
+
WHERE entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL`,
|
|
1038
|
+
[recheckCutoff, recheckCutoff, entityId]
|
|
1039
|
+
);
|
|
1040
|
+
return { eligible: Number(row?.eligible ?? 0), deferred: Number(row?.deferred ?? 0) };
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Sets okf_type only when currently NULL — additive by construction: a
|
|
1044
|
+
* concurrently-typed fact is never overwritten. Returns changes for the caller
|
|
1045
|
+
* to distinguish applied vs skipped.
|
|
1046
|
+
*/
|
|
1047
|
+
async updateOkfType(id, entityId, okfType, tx) {
|
|
1048
|
+
const result = await tx.runAsync(
|
|
1049
|
+
`UPDATE ${this.prefix}entries SET okf_type = ?
|
|
1050
|
+
WHERE id = ? AND entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL`,
|
|
1051
|
+
[okfType, id, entityId]
|
|
1052
|
+
);
|
|
1053
|
+
return { changes: result.changes };
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Stamps the backfill recheck cooldown. NEVER touches updated_at — import
|
|
1057
|
+
* merge resolution is last-write-wins on updated_at and a bump here would
|
|
1058
|
+
* make an unchanged local fact beat a genuinely newer remote edit.
|
|
1059
|
+
*/
|
|
1060
|
+
async markOntologyChecked(ids, entityId, now, tx) {
|
|
1061
|
+
if (ids.length === 0) return;
|
|
1062
|
+
for (let i = 0; i < ids.length; i += this.chunkSize) {
|
|
1063
|
+
const chunk = ids.slice(i, i + this.chunkSize);
|
|
1064
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
1065
|
+
await tx.runAsync(
|
|
1066
|
+
`UPDATE ${this.prefix}entries SET ontology_checked_at = ?
|
|
1067
|
+
WHERE id IN (${placeholders}) AND entity_id = ? AND deleted_at IS NULL`,
|
|
1068
|
+
[now, ...chunk, entityId]
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Lightweight full-breadth title index (id, title, okf_type) over all live
|
|
1074
|
+
* typed facts. Used by ontology backfill edge resolution — a recent-N window
|
|
1075
|
+
* would miss an old fact's contemporaries. Untyped rows are excluded: they
|
|
1076
|
+
* can never pass the edge target-type check, and batch facts typed during
|
|
1077
|
+
* the run are re-added to the in-memory index by the caller.
|
|
1078
|
+
*/
|
|
1079
|
+
async findTitleIndexByEntityId(entityId, tx) {
|
|
1080
|
+
const executor = this.getExecutor(tx);
|
|
1081
|
+
const rows = await executor.getAllAsync(
|
|
1082
|
+
`SELECT id, title, okf_type FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NULL AND okf_type IS NOT NULL`,
|
|
1083
|
+
[entityId]
|
|
1084
|
+
);
|
|
1085
|
+
return rows.map((r) => ({ id: r.id, title: r.title, okf_type: r.okf_type ?? null }));
|
|
1086
|
+
}
|
|
955
1087
|
};
|
|
956
1088
|
|
|
957
1089
|
// src/repositories/OutboxRepository.ts
|
|
@@ -1372,6 +1504,7 @@ var CONFIDENCE_RANK = {
|
|
|
1372
1504
|
var EdgeRepository = class extends BaseRepository {
|
|
1373
1505
|
/**
|
|
1374
1506
|
* Insert an edge, silently skipping on primary-key or uniqueness conflicts.
|
|
1507
|
+
* Returns true when a row was inserted, false when skipped as a duplicate.
|
|
1375
1508
|
* Throws when the insert was skipped due to an id collision with a different edge tuple.
|
|
1376
1509
|
*/
|
|
1377
1510
|
async addIgnoreDuplicate(edge, tx) {
|
|
@@ -1381,17 +1514,18 @@ var EdgeRepository = class extends BaseRepository {
|
|
|
1381
1514
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1382
1515
|
[edge.id, edge.entity_id, edge.source_id, edge.target_id, edge.edge_type, edge.created_at]
|
|
1383
1516
|
);
|
|
1384
|
-
if (result.changes > 0) return;
|
|
1517
|
+
if (result.changes > 0) return true;
|
|
1385
1518
|
const existing = await executor.getFirstAsync(
|
|
1386
1519
|
`SELECT entity_id, source_id, target_id, edge_type FROM ${this.prefix}edges WHERE id = ?`,
|
|
1387
1520
|
[edge.id]
|
|
1388
1521
|
);
|
|
1389
|
-
if (!existing) return;
|
|
1522
|
+
if (!existing) return false;
|
|
1390
1523
|
if (String(existing.entity_id) !== edge.entity_id || String(existing.source_id) !== edge.source_id || String(existing.target_id) !== edge.target_id || String(existing.edge_type) !== edge.edge_type) {
|
|
1391
1524
|
throw new Error(
|
|
1392
1525
|
`Edge id collision: ${JSON.stringify(edge.id)} already exists with a different (entity_id, source_id, target_id, edge_type) tuple`
|
|
1393
1526
|
);
|
|
1394
1527
|
}
|
|
1528
|
+
return false;
|
|
1395
1529
|
}
|
|
1396
1530
|
async getByEntityId(entityId, tx) {
|
|
1397
1531
|
const executor = this.getExecutor(tx);
|
|
@@ -1591,7 +1725,8 @@ var OntologyService = class {
|
|
|
1591
1725
|
return { okf_type: canonical, edges };
|
|
1592
1726
|
}
|
|
1593
1727
|
async resolveAndPersistEdges(entityId, sourceId, sourceType, edges, manifest, titleIndex, tx, now) {
|
|
1594
|
-
if (!sourceType || edges.length === 0) return;
|
|
1728
|
+
if (!sourceType || edges.length === 0) return 0;
|
|
1729
|
+
let persisted = 0;
|
|
1595
1730
|
for (const edge of edges) {
|
|
1596
1731
|
const def = resolveEdgeDefinition(edge.edge_type, manifest);
|
|
1597
1732
|
if (!def || def.source_type.toLowerCase() !== sourceType.toLowerCase()) continue;
|
|
@@ -1607,8 +1742,10 @@ var OntologyService = class {
|
|
|
1607
1742
|
edge_type: def.type,
|
|
1608
1743
|
created_at: now
|
|
1609
1744
|
};
|
|
1610
|
-
await this.edgeRepo.addIgnoreDuplicate(wikiEdge, tx);
|
|
1745
|
+
const inserted = await this.edgeRepo.addIgnoreDuplicate(wikiEdge, tx);
|
|
1746
|
+
if (inserted) persisted++;
|
|
1611
1747
|
}
|
|
1748
|
+
return persisted;
|
|
1612
1749
|
}
|
|
1613
1750
|
};
|
|
1614
1751
|
|
|
@@ -1651,7 +1788,7 @@ var WikiMemory = class {
|
|
|
1651
1788
|
constructor(db, options) {
|
|
1652
1789
|
/** Emits `__testAccess` console warning at most once per instance when NODE_ENV ≠ "test". */
|
|
1653
1790
|
__privateAdd(this, _testAccessNonTestEnvWarned, false);
|
|
1654
|
-
this.db = db;
|
|
1791
|
+
this.db = withSerializedTransactions(db);
|
|
1655
1792
|
this.options = options;
|
|
1656
1793
|
this.prefix = options.config?.tablePrefix ?? "llm_wiki_";
|
|
1657
1794
|
if (!TABLE_PREFIX_PATTERN.test(this.prefix)) {
|
|
@@ -1659,12 +1796,12 @@ var WikiMemory = class {
|
|
|
1659
1796
|
`Invalid tablePrefix: ${JSON.stringify(this.prefix)}. Must match ${TABLE_PREFIX_PATTERN} (letter, then alphanumeric/underscore, ending in "_", max 32 chars total).`
|
|
1660
1797
|
);
|
|
1661
1798
|
}
|
|
1662
|
-
this.outboxRepo = new OutboxRepository(db, this.prefix, !!options.config?.enableOutbox);
|
|
1663
|
-
this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
|
|
1664
|
-
this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
|
|
1665
|
-
this.eventRepo = new EventRepository(db, this.prefix);
|
|
1666
|
-
this.edgeRepo = new EdgeRepository(db, this.prefix);
|
|
1667
|
-
this.metadataRepo = new MetadataRepository(db, this.prefix);
|
|
1799
|
+
this.outboxRepo = new OutboxRepository(this.db, this.prefix, !!options.config?.enableOutbox);
|
|
1800
|
+
this.entryRepo = new EntryRepository(this.db, this.prefix, this.outboxRepo);
|
|
1801
|
+
this.taskRepo = new TaskRepository(this.db, this.prefix, this.outboxRepo);
|
|
1802
|
+
this.eventRepo = new EventRepository(this.db, this.prefix);
|
|
1803
|
+
this.edgeRepo = new EdgeRepository(this.db, this.prefix);
|
|
1804
|
+
this.metadataRepo = new MetadataRepository(this.db, this.prefix);
|
|
1668
1805
|
this.ontologyService = new OntologyService(
|
|
1669
1806
|
this.metadataRepo,
|
|
1670
1807
|
this.edgeRepo,
|
|
@@ -1848,6 +1985,24 @@ var WikiMemory = class {
|
|
|
1848
1985
|
async runHeal(entityId, options) {
|
|
1849
1986
|
return this.maintenanceService.runHeal(entityId, options);
|
|
1850
1987
|
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Types already-persisted untyped facts (okf_type IS NULL) in place via one
|
|
1990
|
+
* librarian-style LLM call. Strictly additive: never creates, deletes, or
|
|
1991
|
+
* rewrites facts; never overwrites an existing okf_type. Free when there is
|
|
1992
|
+
* nothing to do (ontology off, or zero eligible untyped facts).
|
|
1993
|
+
*
|
|
1994
|
+
* Hosts own the trigger cadence (e.g. after each sync); loop `while
|
|
1995
|
+
* result.remaining > 0` for convergence. Throws WikiBusyError when another
|
|
1996
|
+
* backfill (or conflicting maintenance op) is running for the entity.
|
|
1997
|
+
*
|
|
1998
|
+
* @param options.promptOverride - Applies only to this call. For persistent
|
|
1999
|
+
* customization set `options.config.prompts.ontologyBackfillSystemPrompt`.
|
|
2000
|
+
* @param options.batchSize - Facts per run (default 25) for providers with
|
|
2001
|
+
* tighter context limits.
|
|
2002
|
+
*/
|
|
2003
|
+
async runOntologyBackfill(entityId, options) {
|
|
2004
|
+
return this.maintenanceService.runOntologyBackfill(entityId, options);
|
|
2005
|
+
}
|
|
1851
2006
|
async runReembed(entityId, opts) {
|
|
1852
2007
|
return this.maintenanceService.runReembed(entityId, opts);
|
|
1853
2008
|
}
|