@equationalapplications/core-llm-wiki 4.15.3 → 4.17.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 +96 -0
- package/dist/{chunk-J4GBC6CP.mjs → chunk-M74FCWLB.mjs} +238 -46
- package/dist/chunk-M74FCWLB.mjs.map +1 -0
- package/dist/index.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +621 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +388 -17
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-NH1_Aigh.d.mts → testing-D02cdI9A.d.mts} +194 -16
- package/dist/{testing-NH1_Aigh.d.ts → testing-D02cdI9A.d.ts} +194 -16
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +155 -43
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-J4GBC6CP.mjs.map +0 -1
package/dist/testing.js
CHANGED
|
@@ -796,6 +796,11 @@ After running the migration SQL, restart your application.`
|
|
|
796
796
|
}
|
|
797
797
|
};
|
|
798
798
|
|
|
799
|
+
// src/utils/ontology.ts
|
|
800
|
+
function normalizeTitleKey(title) {
|
|
801
|
+
return title.trim().toLowerCase().replace(/\s+/g, " ");
|
|
802
|
+
}
|
|
803
|
+
|
|
799
804
|
// src/utils/ids.ts
|
|
800
805
|
function generateId(prefix = "") {
|
|
801
806
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
@@ -846,30 +851,54 @@ var PromptService = class {
|
|
|
846
851
|
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
847
852
|
});
|
|
848
853
|
}
|
|
849
|
-
|
|
854
|
+
hasOntologyPlaceholders(template) {
|
|
855
|
+
return /\{\{\s*ontology(?:Manifest|ModeInstructions)\s*\}\}/.test(template);
|
|
856
|
+
}
|
|
857
|
+
buildSystemPrompt(template, variables, ontologyContext) {
|
|
858
|
+
const shouldHydrate = Object.keys(variables).some(
|
|
859
|
+
(key) => new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`).test(template)
|
|
860
|
+
) || ontologyContext != null && this.hasOntologyPlaceholders(template);
|
|
861
|
+
const hydrated = shouldHydrate ? this.hydrate(template, { ...variables, ...ontologyContext ?? {} }) : template;
|
|
862
|
+
return this.hasOntologyPlaceholders(template) ? ontologyContext != null ? hydrated : hydrated.replace(/\{\{\s*ontology(?:Manifest|ModeInstructions)\s*\}\}/g, "") : this.appendOntology(hydrated, ontologyContext);
|
|
863
|
+
}
|
|
864
|
+
appendOntology(systemPrompt, ctx) {
|
|
865
|
+
if (!ctx) return systemPrompt;
|
|
866
|
+
return `${systemPrompt}
|
|
867
|
+
|
|
868
|
+
${ctx.ontologyModeInstructions}`;
|
|
869
|
+
}
|
|
870
|
+
buildIngestPrompt(documentChunk, runtimeOverride, ontologyContext) {
|
|
850
871
|
const template = runtimeOverride ?? this.globalOverrides?.ingestSystemPrompt ?? INGEST_SYSTEM_PROMPT;
|
|
851
|
-
|
|
872
|
+
const hasDocumentChunk = /\{\{\s*documentChunk\s*\}\}/.test(template);
|
|
873
|
+
if (hasDocumentChunk || this.hasOntologyPlaceholders(template)) {
|
|
852
874
|
return {
|
|
853
|
-
systemPrompt: this.
|
|
854
|
-
userPrompt: "Please extract the facts."
|
|
875
|
+
systemPrompt: this.buildSystemPrompt(template, { documentChunk }, ontologyContext),
|
|
876
|
+
userPrompt: hasDocumentChunk ? "Please extract the facts." : `Document Chunk:
|
|
877
|
+
${documentChunk}`
|
|
855
878
|
};
|
|
856
879
|
}
|
|
857
880
|
return {
|
|
858
|
-
systemPrompt: template,
|
|
881
|
+
systemPrompt: this.appendOntology(template, ontologyContext),
|
|
859
882
|
userPrompt: `Document Chunk:
|
|
860
883
|
${documentChunk}`
|
|
861
884
|
};
|
|
862
885
|
}
|
|
863
|
-
buildLibrarianPrompt(events, currentFacts, runtimeOverride) {
|
|
886
|
+
buildLibrarianPrompt(events, currentFacts, runtimeOverride, ontologyContext) {
|
|
864
887
|
const template = runtimeOverride ?? this.globalOverrides?.librarianSystemPrompt ?? LIBRARIAN_SYSTEM_PROMPT;
|
|
865
|
-
|
|
888
|
+
const hasEvents = /\{\{\s*events\s*\}\}/.test(template);
|
|
889
|
+
const hasCurrentFacts = /\{\{\s*currentFacts\s*\}\}/.test(template);
|
|
890
|
+
if (hasEvents || hasCurrentFacts || this.hasOntologyPlaceholders(template)) {
|
|
866
891
|
return {
|
|
867
|
-
systemPrompt: this.
|
|
868
|
-
userPrompt: "Please synthesize the context."
|
|
892
|
+
systemPrompt: this.buildSystemPrompt(template, { events, currentFacts }, ontologyContext),
|
|
893
|
+
userPrompt: hasEvents || hasCurrentFacts ? "Please synthesize the context." : `Events:
|
|
894
|
+
${JSON.stringify(events, null, 2)}
|
|
895
|
+
|
|
896
|
+
Current Facts:
|
|
897
|
+
${JSON.stringify(currentFacts, null, 2)}`
|
|
869
898
|
};
|
|
870
899
|
}
|
|
871
900
|
return {
|
|
872
|
-
systemPrompt: template,
|
|
901
|
+
systemPrompt: this.appendOntology(template, ontologyContext),
|
|
873
902
|
userPrompt: `Events:
|
|
874
903
|
${JSON.stringify(events, null, 2)}
|
|
875
904
|
|
|
@@ -902,7 +931,7 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
902
931
|
|
|
903
932
|
// src/services/IngestionService.ts
|
|
904
933
|
var IngestionService = class {
|
|
905
|
-
constructor(db, prefix, options, entryRepo, searchService, jobManager, embeddingService, promptService) {
|
|
934
|
+
constructor(db, prefix, options, entryRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
906
935
|
this.db = db;
|
|
907
936
|
this.prefix = prefix;
|
|
908
937
|
this.options = options;
|
|
@@ -910,6 +939,7 @@ var IngestionService = class {
|
|
|
910
939
|
this.searchService = searchService;
|
|
911
940
|
this.jobManager = jobManager;
|
|
912
941
|
this.embeddingService = embeddingService;
|
|
942
|
+
this.ontologyService = ontologyService;
|
|
913
943
|
this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
|
|
914
944
|
}
|
|
915
945
|
async ingestDocument(entityId, params) {
|
|
@@ -934,23 +964,33 @@ var IngestionService = class {
|
|
|
934
964
|
if (chunks.length === 0) return { truncated: false, chunks: 0 };
|
|
935
965
|
const chunkResults = await withConcurrency(
|
|
936
966
|
chunks.map((chunk) => async () => {
|
|
937
|
-
const
|
|
967
|
+
const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
|
|
968
|
+
const { systemPrompt, userPrompt } = this.promptService.buildIngestPrompt(
|
|
969
|
+
chunk,
|
|
970
|
+
params.promptOverride,
|
|
971
|
+
ontologyContext
|
|
972
|
+
);
|
|
938
973
|
const responseText = await this.options.llmProvider.generateText({ systemPrompt, userPrompt });
|
|
939
974
|
const result = parseJsonResponse(responseText);
|
|
940
|
-
return
|
|
975
|
+
return {
|
|
976
|
+
facts: (Array.isArray(result.facts) ? result.facts : []).map(validateFact).filter((f) => f !== null),
|
|
977
|
+
ontology_updates: result.ontology_updates
|
|
978
|
+
};
|
|
941
979
|
}),
|
|
942
980
|
chunkConcurrency
|
|
943
981
|
);
|
|
944
982
|
const seen = /* @__PURE__ */ new Set();
|
|
945
|
-
const
|
|
946
|
-
for (const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
983
|
+
const orderedChunkFacts = [];
|
|
984
|
+
for (const chunkResult of chunkResults) {
|
|
985
|
+
const dedupedFacts = [];
|
|
986
|
+
for (const fact of chunkResult.facts) {
|
|
987
|
+
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
988
|
+
if (!seen.has(normalizedTitle)) {
|
|
989
|
+
seen.add(normalizedTitle);
|
|
990
|
+
dedupedFacts.push(fact);
|
|
952
991
|
}
|
|
953
992
|
}
|
|
993
|
+
orderedChunkFacts.push({ facts: dedupedFacts, ontology_updates: chunkResult.ontology_updates });
|
|
954
994
|
}
|
|
955
995
|
const now = Date.now();
|
|
956
996
|
const insertedFacts = [];
|
|
@@ -958,26 +998,63 @@ var IngestionService = class {
|
|
|
958
998
|
await this.db.withTransactionAsync(async (tx) => {
|
|
959
999
|
deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
|
|
960
1000
|
await this.entryRepo.softDeleteBySource(entityId, tx, sourceRef, null);
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1001
|
+
const titleIndex = /* @__PURE__ */ new Map();
|
|
1002
|
+
const pendingEdges = [];
|
|
1003
|
+
const existingFacts = await this.entryRepo.findRecentByEntityId(entityId, 500, tx);
|
|
1004
|
+
for (const existing of existingFacts) {
|
|
1005
|
+
titleIndex.set(normalizeTitleKey(existing.title), {
|
|
1006
|
+
id: existing.id,
|
|
1007
|
+
okf_type: existing.okf_type ?? null
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
let ontologyState = await this.ontologyService?.getEffectiveState(entityId, tx) ?? { mode: "off", manifest: { node_types: [], edge_types: [] } };
|
|
1011
|
+
let { mode, manifest } = ontologyState;
|
|
1012
|
+
for (const { facts, ontology_updates } of orderedChunkFacts) {
|
|
1013
|
+
if (mode === "emergent" && ontology_updates && this.ontologyService) {
|
|
1014
|
+
manifest = await this.ontologyService.mergeEmergentUpdates(entityId, ontology_updates, tx);
|
|
1015
|
+
ontologyState = await this.ontologyService.getEffectiveState(entityId, tx);
|
|
1016
|
+
mode = ontologyState.mode;
|
|
1017
|
+
}
|
|
1018
|
+
for (const fact of facts) {
|
|
1019
|
+
const ontologyFact = fact;
|
|
1020
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest) ?? { okf_type: null, edges: [] };
|
|
1021
|
+
const id = generateId("fact_");
|
|
1022
|
+
const wikiFact = {
|
|
1023
|
+
id,
|
|
1024
|
+
entity_id: entityId,
|
|
1025
|
+
title: fact.title,
|
|
1026
|
+
body: fact.body,
|
|
1027
|
+
tags: fact.tags,
|
|
1028
|
+
confidence: fact.confidence,
|
|
1029
|
+
source_type: "immutable_document",
|
|
1030
|
+
source_hash: sourceHash,
|
|
1031
|
+
source_ref: sourceRef,
|
|
1032
|
+
created_at: now,
|
|
1033
|
+
updated_at: now,
|
|
1034
|
+
last_accessed_at: null,
|
|
1035
|
+
access_count: 0,
|
|
1036
|
+
deleted_at: null,
|
|
1037
|
+
okf_type: normalized.okf_type
|
|
1038
|
+
};
|
|
1039
|
+
await this.entryRepo.upsert(wikiFact, tx);
|
|
1040
|
+
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1041
|
+
titleIndex.set(normalizeTitleKey(fact.title), { id, okf_type: normalized.okf_type });
|
|
1042
|
+
if (normalized.edges.length > 0) {
|
|
1043
|
+
pendingEdges.push({ sourceId: id, sourceType: normalized.okf_type, edges: normalized.edges });
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
for (const item of pendingEdges) {
|
|
1048
|
+
await this.ontologyService?.resolveAndPersistEdges(
|
|
1049
|
+
entityId,
|
|
1050
|
+
item.sourceId,
|
|
1051
|
+
item.sourceType,
|
|
1052
|
+
item.edges ?? [],
|
|
1053
|
+
manifest,
|
|
1054
|
+
titleIndex,
|
|
1055
|
+
tx,
|
|
1056
|
+
now
|
|
1057
|
+
);
|
|
981
1058
|
}
|
|
982
1059
|
});
|
|
983
1060
|
await this.searchService.sync(entityId);
|
|
@@ -1032,7 +1109,7 @@ function parseEmbedding(blob, text) {
|
|
|
1032
1109
|
var FUZZY_THRESHOLD = 0.5;
|
|
1033
1110
|
var MIN_TOKENS_TO_QUALIFY = 3;
|
|
1034
1111
|
var MaintenanceService = class {
|
|
1035
|
-
constructor(db, prefix, options, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService) {
|
|
1112
|
+
constructor(db, prefix, options, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1036
1113
|
this.db = db;
|
|
1037
1114
|
this.prefix = prefix;
|
|
1038
1115
|
this.options = options;
|
|
@@ -1043,6 +1120,7 @@ var MaintenanceService = class {
|
|
|
1043
1120
|
this.searchService = searchService;
|
|
1044
1121
|
this.jobManager = jobManager;
|
|
1045
1122
|
this.embeddingService = embeddingService;
|
|
1123
|
+
this.ontologyService = ontologyService;
|
|
1046
1124
|
this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
|
|
1047
1125
|
}
|
|
1048
1126
|
async runPrune(entityId, options) {
|
|
@@ -1265,21 +1343,36 @@ var MaintenanceService = class {
|
|
|
1265
1343
|
tags: typeof rest.tags === "string" ? JSON.parse(rest.tags) : rest.tags
|
|
1266
1344
|
};
|
|
1267
1345
|
});
|
|
1346
|
+
const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
|
|
1268
1347
|
const { systemPrompt, userPrompt } = this.promptService.buildLibrarianPrompt(
|
|
1269
1348
|
events.reverse(),
|
|
1270
1349
|
currentFacts,
|
|
1271
|
-
promptOverride
|
|
1350
|
+
promptOverride,
|
|
1351
|
+
ontologyContext
|
|
1272
1352
|
);
|
|
1273
1353
|
const responseText = await this.options.llmProvider.generateText({ systemPrompt, userPrompt });
|
|
1274
1354
|
const result = parseJsonResponse(responseText);
|
|
1275
1355
|
const facts = Array.isArray(result.facts) ? result.facts : [];
|
|
1276
1356
|
const tasks = Array.isArray(result.tasks) ? result.tasks : [];
|
|
1357
|
+
const ontologyUpdates = result.ontology_updates;
|
|
1277
1358
|
const validFacts = facts.map(validateFact).filter((f) => f !== null);
|
|
1278
1359
|
const validTasks = tasks.map(validateTask).filter((t) => t !== null);
|
|
1279
1360
|
const now = Date.now();
|
|
1280
1361
|
const insertedFacts = [];
|
|
1281
1362
|
await this.db.withTransactionAsync(async (tx) => {
|
|
1363
|
+
let { mode, manifest } = await this.ontologyService?.getEffectiveState(entityId, tx) ?? { mode: "off", manifest: { node_types: [], edge_types: [] } };
|
|
1364
|
+
if (mode === "emergent" && ontologyUpdates && this.ontologyService) {
|
|
1365
|
+
manifest = await this.ontologyService.mergeEmergentUpdates(entityId, ontologyUpdates, tx);
|
|
1366
|
+
}
|
|
1367
|
+
const titleIndex = /* @__PURE__ */ new Map();
|
|
1368
|
+
for (const existing of currentFactsRows) {
|
|
1369
|
+
titleIndex.set(normalizeTitleKey(existing.title), {
|
|
1370
|
+
id: existing.id,
|
|
1371
|
+
okf_type: existing.okf_type ?? null
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1282
1374
|
const factsForDedupe = await this.entryRepo.findRecentByEntityId(entityId, 100, tx);
|
|
1375
|
+
const pendingEdges = [];
|
|
1283
1376
|
for (const fact of validFacts) {
|
|
1284
1377
|
const newTokens = titleTokens(fact.title);
|
|
1285
1378
|
let skip = false;
|
|
@@ -1296,6 +1389,8 @@ var MaintenanceService = class {
|
|
|
1296
1389
|
}
|
|
1297
1390
|
}
|
|
1298
1391
|
if (skip) continue;
|
|
1392
|
+
const ontologyFact = fact;
|
|
1393
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest) ?? { okf_type: null, edges: [] };
|
|
1299
1394
|
const id = generateId("fact_");
|
|
1300
1395
|
const factObj = {
|
|
1301
1396
|
id,
|
|
@@ -1311,11 +1406,28 @@ var MaintenanceService = class {
|
|
|
1311
1406
|
updated_at: now,
|
|
1312
1407
|
last_accessed_at: null,
|
|
1313
1408
|
access_count: 0,
|
|
1314
|
-
deleted_at: null
|
|
1409
|
+
deleted_at: null,
|
|
1410
|
+
okf_type: normalized.okf_type
|
|
1315
1411
|
};
|
|
1316
1412
|
await this.entryRepo.upsert(factObj, tx);
|
|
1317
1413
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1318
1414
|
factsForDedupe.push(factObj);
|
|
1415
|
+
titleIndex.set(normalizeTitleKey(fact.title), { id, okf_type: normalized.okf_type });
|
|
1416
|
+
if (normalized.edges.length > 0) {
|
|
1417
|
+
pendingEdges.push({ sourceId: id, sourceType: normalized.okf_type, edges: normalized.edges });
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
for (const item of pendingEdges) {
|
|
1421
|
+
await this.ontologyService?.resolveAndPersistEdges(
|
|
1422
|
+
entityId,
|
|
1423
|
+
item.sourceId,
|
|
1424
|
+
item.sourceType,
|
|
1425
|
+
item.edges ?? [],
|
|
1426
|
+
manifest,
|
|
1427
|
+
titleIndex,
|
|
1428
|
+
tx,
|
|
1429
|
+
now
|
|
1430
|
+
);
|
|
1319
1431
|
}
|
|
1320
1432
|
for (const task of validTasks) {
|
|
1321
1433
|
const id = generateId("task_");
|