@equationalapplications/core-llm-wiki 7.2.0 → 7.3.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 +23 -0
- package/dist/{chunk-TTECJ3S3.mjs → chunk-IAYO63DQ.mjs} +346 -41
- package/dist/chunk-IAYO63DQ.mjs.map +1 -0
- package/dist/index.d.mts +3 -5
- package/dist/index.d.ts +3 -5
- package/dist/index.js +388 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DYeMd9d1.d.mts → testing-ht1qOvcD.d.mts} +98 -6
- package/dist/{testing-DYeMd9d1.d.ts → testing-ht1qOvcD.d.ts} +98 -6
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +336 -38
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +5 -5
- package/dist/chunk-TTECJ3S3.mjs.map +0 -1
|
@@ -336,20 +336,27 @@ function mergeOntologyUpdates(current, updates) {
|
|
|
336
336
|
function validateInlineEdges(sourceType, _targetType, edges, manifest, opts) {
|
|
337
337
|
const strict = opts?.strict === true;
|
|
338
338
|
const entityId = opts?.entityId ?? "";
|
|
339
|
+
const drops = opts?.drops;
|
|
340
|
+
const drop = (reason, edgeType) => {
|
|
341
|
+
drops?.push({ reason, sourceId: null, edgeType, sourceNodeType: sourceType, targetNodeType: null });
|
|
342
|
+
};
|
|
339
343
|
if (!Array.isArray(edges)) {
|
|
340
344
|
if (strict) throw new WikiStrictOntologyViolation(entityId, "edge", "");
|
|
345
|
+
drop("invalid_shape", null);
|
|
341
346
|
return [];
|
|
342
347
|
}
|
|
343
348
|
const valid = [];
|
|
344
349
|
for (const edge of edges) {
|
|
345
350
|
if (typeof edge?.edge_type !== "string" || typeof edge?.target_title !== "string") {
|
|
346
351
|
if (strict) throw new WikiStrictOntologyViolation(entityId, "edge", String(edge?.edge_type ?? ""));
|
|
352
|
+
drop("invalid_shape", typeof edge?.edge_type === "string" ? edge.edge_type : null);
|
|
347
353
|
continue;
|
|
348
354
|
}
|
|
349
355
|
const defs = resolveEdgeDefinitions(edge.edge_type, manifest);
|
|
350
356
|
const match = defs.find((d) => typeSatisfies(d.source_type, sourceType, manifest));
|
|
351
357
|
if (!match) {
|
|
352
358
|
if (strict) throw new WikiStrictOntologyViolation(entityId, "edge", edge.edge_type);
|
|
359
|
+
drop("type_not_in_manifest", edge.edge_type);
|
|
353
360
|
continue;
|
|
354
361
|
}
|
|
355
362
|
valid.push({ edge_type: match.type, target_title: edge.target_title });
|
|
@@ -1526,6 +1533,17 @@ function validateTask(task) {
|
|
|
1526
1533
|
priority
|
|
1527
1534
|
};
|
|
1528
1535
|
}
|
|
1536
|
+
function factRejectionReason(raw) {
|
|
1537
|
+
const fact = raw;
|
|
1538
|
+
if (typeof fact?.title !== "string" || typeof fact?.body !== "string") return "invalid_shape";
|
|
1539
|
+
if (!clip(fact.title, 80)) return "missing_title";
|
|
1540
|
+
return "missing_body";
|
|
1541
|
+
}
|
|
1542
|
+
function taskRejectionReason(raw) {
|
|
1543
|
+
const task = raw;
|
|
1544
|
+
if (typeof task?.description !== "string") return "invalid_shape";
|
|
1545
|
+
return "missing_description";
|
|
1546
|
+
}
|
|
1529
1547
|
function normalizeSourceRef(value) {
|
|
1530
1548
|
if (typeof value !== "string") return null;
|
|
1531
1549
|
const cleaned = value.replace(/[^A-Za-z0-9._\- ]/g, "").trim().slice(0, 255);
|
|
@@ -1765,6 +1783,104 @@ function applyBodyTruncation(candidates, attemptLevel, bodyTruncationChars) {
|
|
|
1765
1783
|
var DEFAULT_MAX_CHUNK_LENGTH = 12e3;
|
|
1766
1784
|
var DEFAULT_CHUNK_OVERLAP = 400;
|
|
1767
1785
|
|
|
1786
|
+
// src/utils/diagnostics.ts
|
|
1787
|
+
var SEVERITY = {
|
|
1788
|
+
ingest_chunk_failed: "warn",
|
|
1789
|
+
fact_rejected: "warn",
|
|
1790
|
+
task_rejected: "warn",
|
|
1791
|
+
fact_deduplicated: "info",
|
|
1792
|
+
edge_dropped: "warn",
|
|
1793
|
+
embedding_failed: "warn",
|
|
1794
|
+
hook_failed: "warn",
|
|
1795
|
+
background_job_failed: "error",
|
|
1796
|
+
heal_skipped: "warn",
|
|
1797
|
+
grounding_missing: "warn",
|
|
1798
|
+
grounding_failed: "warn",
|
|
1799
|
+
classification_low_confidence: "info",
|
|
1800
|
+
classification_invalid: "warn"
|
|
1801
|
+
};
|
|
1802
|
+
var MESSAGE = {
|
|
1803
|
+
ingest_chunk_failed: "One or more document chunks failed to parse or generate.",
|
|
1804
|
+
fact_rejected: "An extracted fact failed validation and was not written.",
|
|
1805
|
+
task_rejected: "An extracted task failed validation and was not written.",
|
|
1806
|
+
fact_deduplicated: "An extracted fact duplicated an existing fact and was not written.",
|
|
1807
|
+
edge_dropped: "An extracted edge could not be resolved against the ontology and was not written.",
|
|
1808
|
+
embedding_failed: "A fact could not be embedded.",
|
|
1809
|
+
hook_failed: "A host hook threw or rejected.",
|
|
1810
|
+
background_job_failed: "A background maintenance job failed.",
|
|
1811
|
+
heal_skipped: "Heal skipped a candidate fact.",
|
|
1812
|
+
grounding_missing: "A fact carried no qualifying evidence and was stored as a draft.",
|
|
1813
|
+
grounding_failed: "A fact carried evidence not found in its source and was stored as a draft.",
|
|
1814
|
+
classification_low_confidence: "A classifier answer was below the confidence threshold and was not applied.",
|
|
1815
|
+
classification_invalid: "A classifier answer was invalid and was not applied."
|
|
1816
|
+
};
|
|
1817
|
+
var warnedNonFunction = /* @__PURE__ */ new WeakSet();
|
|
1818
|
+
function copyDetail(detail) {
|
|
1819
|
+
const copy = { ...detail };
|
|
1820
|
+
if (detail.chunkIndexes) copy.chunkIndexes = detail.chunkIndexes.slice();
|
|
1821
|
+
return copy;
|
|
1822
|
+
}
|
|
1823
|
+
function emitDiagnostic(options, input) {
|
|
1824
|
+
const hook = options.onDiagnostic;
|
|
1825
|
+
if (hook === void 0 || hook === null) return;
|
|
1826
|
+
if (typeof hook !== "function") {
|
|
1827
|
+
if (!warnedNonFunction.has(options)) {
|
|
1828
|
+
warnedNonFunction.add(options);
|
|
1829
|
+
console.warn("[WikiMemory] onDiagnostic is not a function; diagnostics are disabled.");
|
|
1830
|
+
}
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
const diagnostic = {
|
|
1834
|
+
code: input.code,
|
|
1835
|
+
severity: SEVERITY[input.code],
|
|
1836
|
+
operation: input.operation,
|
|
1837
|
+
trigger: input.trigger,
|
|
1838
|
+
entityId: input.entityId,
|
|
1839
|
+
at: Date.now(),
|
|
1840
|
+
message: MESSAGE[input.code],
|
|
1841
|
+
...input.detail ? { detail: copyDetail(input.detail) } : {}
|
|
1842
|
+
};
|
|
1843
|
+
try {
|
|
1844
|
+
const returned = hook(diagnostic);
|
|
1845
|
+
if (returned !== void 0) {
|
|
1846
|
+
Promise.resolve(returned).catch((err) => {
|
|
1847
|
+
console.warn("[WikiMemory] onDiagnostic hook rejected:", err);
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
console.warn("[WikiMemory] onDiagnostic hook threw:", err);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
var DiagnosticBuffer = class {
|
|
1855
|
+
constructor() {
|
|
1856
|
+
this.items = [];
|
|
1857
|
+
}
|
|
1858
|
+
push(input) {
|
|
1859
|
+
this.items.push(input);
|
|
1860
|
+
}
|
|
1861
|
+
get size() {
|
|
1862
|
+
return this.items.length;
|
|
1863
|
+
}
|
|
1864
|
+
flush(options) {
|
|
1865
|
+
const pending = this.items;
|
|
1866
|
+
this.items = [];
|
|
1867
|
+
for (const input of pending) emitDiagnostic(options, input);
|
|
1868
|
+
}
|
|
1869
|
+
discard() {
|
|
1870
|
+
this.items = [];
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
function edgeDropDiagnostic(drop, ctx) {
|
|
1874
|
+
const detail = { reason: drop.reason };
|
|
1875
|
+
const factId = ctx.factId ?? drop.sourceId;
|
|
1876
|
+
if (factId) detail.factId = factId;
|
|
1877
|
+
if (drop.edgeType) detail.edgeType = drop.edgeType;
|
|
1878
|
+
if (drop.sourceNodeType) detail.sourceNodeType = drop.sourceNodeType;
|
|
1879
|
+
if (drop.targetNodeType) detail.targetNodeType = drop.targetNodeType;
|
|
1880
|
+
if (ctx.sourceRef) detail.sourceRef = ctx.sourceRef;
|
|
1881
|
+
return { code: "edge_dropped", operation: ctx.operation, trigger: ctx.trigger, entityId: ctx.entityId, detail };
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1768
1884
|
// src/services/IngestionService.ts
|
|
1769
1885
|
function zeroChunkResult(duplicateOf) {
|
|
1770
1886
|
const result = {
|
|
@@ -1832,9 +1948,24 @@ var IngestionService = class {
|
|
|
1832
1948
|
try {
|
|
1833
1949
|
const responseText = await this.options.llmProvider.generateText({ systemPrompt, userPrompt });
|
|
1834
1950
|
const result2 = parseJsonResponse(responseText);
|
|
1951
|
+
const rawFacts = Array.isArray(result2.facts) ? result2.facts : [];
|
|
1952
|
+
const facts = [];
|
|
1953
|
+
const itemIndexes = [];
|
|
1954
|
+
const rejected = [];
|
|
1955
|
+
rawFacts.forEach((raw, itemIndex) => {
|
|
1956
|
+
const valid = validateFact(raw);
|
|
1957
|
+
if (valid) {
|
|
1958
|
+
facts.push(valid);
|
|
1959
|
+
itemIndexes.push(itemIndex);
|
|
1960
|
+
} else {
|
|
1961
|
+
rejected.push({ itemIndex, reason: factRejectionReason(raw) });
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1835
1964
|
return {
|
|
1836
1965
|
status: "ok",
|
|
1837
|
-
facts
|
|
1966
|
+
facts,
|
|
1967
|
+
itemIndexes,
|
|
1968
|
+
rejected,
|
|
1838
1969
|
ontology_updates: result2.ontology_updates
|
|
1839
1970
|
};
|
|
1840
1971
|
} catch (e) {
|
|
@@ -1879,23 +2010,48 @@ var IngestionService = class {
|
|
|
1879
2010
|
const failures = [];
|
|
1880
2011
|
const seen = /* @__PURE__ */ new Set();
|
|
1881
2012
|
const orderedChunkFacts = [];
|
|
1882
|
-
|
|
2013
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
2014
|
+
const diagBase = { entityId, operation: "ingest", trigger: "call" };
|
|
2015
|
+
for (const [chunkIndex, slot] of chunkResults.entries()) {
|
|
1883
2016
|
if (slot.status === "failed") {
|
|
1884
2017
|
failedChunks++;
|
|
1885
2018
|
failures.push(slot.error);
|
|
1886
2019
|
continue;
|
|
1887
2020
|
}
|
|
1888
2021
|
ingestedChunks++;
|
|
2022
|
+
for (const r of slot.rejected) {
|
|
2023
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { sourceRef, chunkIndex, itemIndex: r.itemIndex, reason: r.reason } });
|
|
2024
|
+
}
|
|
1889
2025
|
const dedupedFacts = [];
|
|
1890
|
-
|
|
2026
|
+
slot.facts.forEach((fact, k) => {
|
|
1891
2027
|
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
1892
2028
|
if (!seen.has(normalizedTitle)) {
|
|
1893
2029
|
seen.add(normalizedTitle);
|
|
1894
2030
|
dedupedFacts.push(fact);
|
|
2031
|
+
} else {
|
|
2032
|
+
diagBuffer.push({
|
|
2033
|
+
...diagBase,
|
|
2034
|
+
code: "fact_deduplicated",
|
|
2035
|
+
detail: { sourceRef, chunkIndex, itemIndex: slot.itemIndexes[k], reason: "exact_title" }
|
|
2036
|
+
});
|
|
1895
2037
|
}
|
|
1896
|
-
}
|
|
2038
|
+
});
|
|
1897
2039
|
orderedChunkFacts.push({ facts: dedupedFacts, ontology_updates: slot.ontology_updates });
|
|
1898
2040
|
}
|
|
2041
|
+
for (const reason of ["parse", "llm"]) {
|
|
2042
|
+
const ofReason = failures.filter((f) => f.source === reason);
|
|
2043
|
+
if (ofReason.length === 0) continue;
|
|
2044
|
+
diagBuffer.push({
|
|
2045
|
+
...diagBase,
|
|
2046
|
+
code: "ingest_chunk_failed",
|
|
2047
|
+
detail: {
|
|
2048
|
+
sourceRef,
|
|
2049
|
+
reason,
|
|
2050
|
+
count: ofReason.length,
|
|
2051
|
+
chunkIndexes: ofReason.map((f) => f.chunkIndex).sort((a, b) => a - b).slice(0, 20)
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
1899
2055
|
if (failedChunks === chunks.length) {
|
|
1900
2056
|
throw new WikiIngestEmptyError({
|
|
1901
2057
|
parseFailures: failures,
|
|
@@ -1908,7 +2064,7 @@ var IngestionService = class {
|
|
|
1908
2064
|
try {
|
|
1909
2065
|
if (failedChunks === 0) {
|
|
1910
2066
|
const fullResult = await this.db.withTransactionAsync(async (tx) => {
|
|
1911
|
-
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx);
|
|
2067
|
+
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer);
|
|
1912
2068
|
});
|
|
1913
2069
|
deletedSourceFactIds.push(...fullResult.deletedSourceFactIds);
|
|
1914
2070
|
insertedFacts.push(...fullResult.insertedFacts);
|
|
@@ -1916,7 +2072,7 @@ var IngestionService = class {
|
|
|
1916
2072
|
const partialResult = await this.db.withTransactionAsync(async (tx) => {
|
|
1917
2073
|
const flat = [];
|
|
1918
2074
|
for (const slot of orderedChunkFacts) flat.push(...slot.facts);
|
|
1919
|
-
return await this.appendPartialFacts(entityId, sourceRef, flat, tx);
|
|
2075
|
+
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer);
|
|
1920
2076
|
});
|
|
1921
2077
|
insertedFacts.push(...partialResult.insertedDescriptors);
|
|
1922
2078
|
}
|
|
@@ -1930,6 +2086,7 @@ var IngestionService = class {
|
|
|
1930
2086
|
}
|
|
1931
2087
|
return zeroChunkResult(canonical);
|
|
1932
2088
|
}
|
|
2089
|
+
diagBuffer.flush(this.options);
|
|
1933
2090
|
await this.searchService.sync(entityId);
|
|
1934
2091
|
if (failedChunks === 0) {
|
|
1935
2092
|
const uniqueDeletedSourceFactIds = Array.from(new Set(deletedSourceFactIds));
|
|
@@ -1938,11 +2095,18 @@ var IngestionService = class {
|
|
|
1938
2095
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
1939
2096
|
} catch (hookErr) {
|
|
1940
2097
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during ingest for ${factId}:`, hookErr);
|
|
2098
|
+
emitDiagnostic(this.options, {
|
|
2099
|
+
entityId,
|
|
2100
|
+
operation: "ingest",
|
|
2101
|
+
trigger: "call",
|
|
2102
|
+
code: "hook_failed",
|
|
2103
|
+
detail: { factId, reason: "on_embedding_persisted" }
|
|
2104
|
+
});
|
|
1941
2105
|
}
|
|
1942
2106
|
}
|
|
1943
2107
|
}
|
|
1944
2108
|
for (const fact of insertedFacts) {
|
|
1945
|
-
await this.embeddingService.embedFact(fact);
|
|
2109
|
+
await this.embeddingService.embedFact(fact, { operation: "ingest", trigger: "call" });
|
|
1946
2110
|
}
|
|
1947
2111
|
this.searchService.evictCache(entityId);
|
|
1948
2112
|
const result = {
|
|
@@ -2088,6 +2252,19 @@ var IngestionService = class {
|
|
|
2088
2252
|
const match = candidates[0];
|
|
2089
2253
|
if (!match) {
|
|
2090
2254
|
if (strictEffective) throw new WikiStrictOntologyViolation(entityId, "edge", edge.type);
|
|
2255
|
+
opts?.diag?.buffer.push({
|
|
2256
|
+
entityId,
|
|
2257
|
+
operation: opts.diag.operation,
|
|
2258
|
+
trigger: "call",
|
|
2259
|
+
code: "edge_dropped",
|
|
2260
|
+
detail: {
|
|
2261
|
+
reason: "manifest_violation",
|
|
2262
|
+
factId: edge.sourceId,
|
|
2263
|
+
edgeType: edge.type,
|
|
2264
|
+
...sourceType ? { sourceNodeType: sourceType } : {},
|
|
2265
|
+
sourceRef: params.sourceRef
|
|
2266
|
+
}
|
|
2267
|
+
});
|
|
2091
2268
|
continue;
|
|
2092
2269
|
}
|
|
2093
2270
|
validEdges.push({
|
|
@@ -2128,7 +2305,7 @@ var IngestionService = class {
|
|
|
2128
2305
|
* On the partial path the caller never invokes this method; the empty
|
|
2129
2306
|
* arrays stay empty.
|
|
2130
2307
|
*/
|
|
2131
|
-
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx) {
|
|
2308
|
+
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer) {
|
|
2132
2309
|
const deletedSourceFactIds = [];
|
|
2133
2310
|
const insertedFacts = [];
|
|
2134
2311
|
deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
|
|
@@ -2153,8 +2330,12 @@ var IngestionService = class {
|
|
|
2153
2330
|
}
|
|
2154
2331
|
for (const fact of facts) {
|
|
2155
2332
|
const ontologyFact = fact;
|
|
2156
|
-
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false }) ?? { okf_type: null, edges: [] };
|
|
2157
2333
|
const id = generateId("fact_");
|
|
2334
|
+
const validationDrops = [];
|
|
2335
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
|
|
2336
|
+
for (const drop of validationDrops) {
|
|
2337
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { entityId, operation: "ingest", trigger: "call", sourceRef, factId: id }));
|
|
2338
|
+
}
|
|
2158
2339
|
hostNodes.push({
|
|
2159
2340
|
id,
|
|
2160
2341
|
type: ontologyFact.okf_type ?? "",
|
|
@@ -2179,6 +2360,7 @@ var IngestionService = class {
|
|
|
2179
2360
|
}
|
|
2180
2361
|
const hostEdges = [];
|
|
2181
2362
|
for (const req of rawEdgeRequests) {
|
|
2363
|
+
const resolveDrops = [];
|
|
2182
2364
|
const resolved = this.ontologyService?.resolveEdges(
|
|
2183
2365
|
entityId,
|
|
2184
2366
|
req.sourceId,
|
|
@@ -2186,8 +2368,12 @@ var IngestionService = class {
|
|
|
2186
2368
|
req.edges,
|
|
2187
2369
|
manifest,
|
|
2188
2370
|
titleIndex,
|
|
2189
|
-
now
|
|
2371
|
+
now,
|
|
2372
|
+
resolveDrops
|
|
2190
2373
|
) ?? [];
|
|
2374
|
+
for (const drop of resolveDrops) {
|
|
2375
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { entityId, operation: "ingest", trigger: "call", sourceRef }));
|
|
2376
|
+
}
|
|
2191
2377
|
for (const e of resolved) {
|
|
2192
2378
|
hostEdges.push({ type: e.edge_type, sourceId: e.source_id, targetId: e.target_id });
|
|
2193
2379
|
}
|
|
@@ -2196,7 +2382,7 @@ var IngestionService = class {
|
|
|
2196
2382
|
entityId,
|
|
2197
2383
|
{ sourceRef, sourceHash, nodes: hostNodes, edges: hostEdges },
|
|
2198
2384
|
tx,
|
|
2199
|
-
{ strict: false }
|
|
2385
|
+
{ strict: false, diag: { buffer: diagBuffer, operation: "ingest" } }
|
|
2200
2386
|
);
|
|
2201
2387
|
return { deletedSourceFactIds, insertedFacts };
|
|
2202
2388
|
}
|
|
@@ -2220,7 +2406,7 @@ var IngestionService = class {
|
|
|
2220
2406
|
* Runs INSIDE the caller's `tx`. Does not open a nested transaction.
|
|
2221
2407
|
* Returns `{ inserted, skippedDuplicate }` for observability.
|
|
2222
2408
|
*/
|
|
2223
|
-
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx) {
|
|
2409
|
+
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer) {
|
|
2224
2410
|
const liveIds = await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false);
|
|
2225
2411
|
const liveFacts = liveIds.length === 0 ? [] : await this.entryRepo.findByIds(liveIds, void 0, tx);
|
|
2226
2412
|
const liveTitles = new Set(liveFacts.map((f) => normalizeTitleKey(f.title)));
|
|
@@ -2231,6 +2417,13 @@ var IngestionService = class {
|
|
|
2231
2417
|
for (const fact of dedupedFacts) {
|
|
2232
2418
|
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
2233
2419
|
if (liveTitles.has(normalizedTitle)) {
|
|
2420
|
+
diagBuffer.push({
|
|
2421
|
+
entityId,
|
|
2422
|
+
operation: "ingest",
|
|
2423
|
+
trigger: "call",
|
|
2424
|
+
code: "fact_deduplicated",
|
|
2425
|
+
detail: { sourceRef, reason: "exact_title" }
|
|
2426
|
+
});
|
|
2234
2427
|
skippedDuplicate++;
|
|
2235
2428
|
continue;
|
|
2236
2429
|
}
|
|
@@ -2817,7 +3010,7 @@ var MaintenanceService = class {
|
|
|
2817
3010
|
permanentlyFailed++;
|
|
2818
3011
|
continue;
|
|
2819
3012
|
}
|
|
2820
|
-
const result = await this.embeddingService.tryEmbedFact(row);
|
|
3013
|
+
const result = await this.embeddingService.tryEmbedFact(row, { operation: "reembed", trigger: "call" });
|
|
2821
3014
|
if (result.ok) embedded++;
|
|
2822
3015
|
else failed++;
|
|
2823
3016
|
}
|
|
@@ -2938,7 +3131,7 @@ var MaintenanceService = class {
|
|
|
2938
3131
|
return { deleted: { entries, tasks: 0 } };
|
|
2939
3132
|
}
|
|
2940
3133
|
/** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
|
|
2941
|
-
async doRunLibrarian(entityId, promptOverride) {
|
|
3134
|
+
async doRunLibrarian(entityId, promptOverride, trigger = "call") {
|
|
2942
3135
|
const events = await this.eventRepo.getRecent(entityId, 50);
|
|
2943
3136
|
const currentFactsRows = await this.entryRepo.findRecentByEntityId(entityId, 100);
|
|
2944
3137
|
const currentFacts = currentFactsRows.map((f) => {
|
|
@@ -2960,8 +3153,25 @@ var MaintenanceService = class {
|
|
|
2960
3153
|
const facts = Array.isArray(result.facts) ? result.facts : [];
|
|
2961
3154
|
const tasks = Array.isArray(result.tasks) ? result.tasks : [];
|
|
2962
3155
|
const ontologyUpdates = result.ontology_updates;
|
|
2963
|
-
const
|
|
2964
|
-
const
|
|
3156
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
3157
|
+
const diagBase = { entityId, operation: "librarian", trigger };
|
|
3158
|
+
const validFacts = [];
|
|
3159
|
+
const validFactItemIndexes = [];
|
|
3160
|
+
facts.forEach((raw, itemIndex) => {
|
|
3161
|
+
const valid = validateFact(raw);
|
|
3162
|
+
if (valid) {
|
|
3163
|
+
validFacts.push(valid);
|
|
3164
|
+
validFactItemIndexes.push(itemIndex);
|
|
3165
|
+
} else {
|
|
3166
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
|
|
3167
|
+
}
|
|
3168
|
+
});
|
|
3169
|
+
const validTasks = [];
|
|
3170
|
+
tasks.forEach((raw, itemIndex) => {
|
|
3171
|
+
const valid = validateTask(raw);
|
|
3172
|
+
if (valid) validTasks.push(valid);
|
|
3173
|
+
else diagBuffer.push({ ...diagBase, code: "task_rejected", detail: { itemIndex, reason: taskRejectionReason(raw) } });
|
|
3174
|
+
});
|
|
2965
3175
|
const now = Date.now();
|
|
2966
3176
|
const insertedFacts = [];
|
|
2967
3177
|
await this.db.withTransactionAsync(async (tx) => {
|
|
@@ -2978,7 +3188,7 @@ var MaintenanceService = class {
|
|
|
2978
3188
|
}
|
|
2979
3189
|
const factsForDedupe = await this.entryRepo.findRecentByEntityId(entityId, 100, tx);
|
|
2980
3190
|
const pendingEdges = [];
|
|
2981
|
-
for (const fact of validFacts) {
|
|
3191
|
+
for (const [k, fact] of validFacts.entries()) {
|
|
2982
3192
|
const newTokens = titleTokens(fact.title);
|
|
2983
3193
|
let skip = false;
|
|
2984
3194
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
@@ -2993,10 +3203,15 @@ var MaintenanceService = class {
|
|
|
2993
3203
|
}
|
|
2994
3204
|
}
|
|
2995
3205
|
}
|
|
2996
|
-
if (skip)
|
|
3206
|
+
if (skip) {
|
|
3207
|
+
diagBuffer.push({ ...diagBase, code: "fact_deduplicated", detail: { itemIndex: validFactItemIndexes[k], reason: "fuzzy_title" } });
|
|
3208
|
+
continue;
|
|
3209
|
+
}
|
|
2997
3210
|
const ontologyFact = fact;
|
|
2998
|
-
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false }) ?? { okf_type: null, edges: [] };
|
|
2999
3211
|
const id = generateId("fact_");
|
|
3212
|
+
const validationDrops = [];
|
|
3213
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
|
|
3214
|
+
for (const drop of validationDrops) diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: id }));
|
|
3000
3215
|
const factObj = {
|
|
3001
3216
|
id,
|
|
3002
3217
|
entity_id: entityId,
|
|
@@ -3023,6 +3238,7 @@ var MaintenanceService = class {
|
|
|
3023
3238
|
}
|
|
3024
3239
|
}
|
|
3025
3240
|
for (const item of pendingEdges) {
|
|
3241
|
+
const resolveDrops = [];
|
|
3026
3242
|
await this.ontologyService?.resolveAndPersistEdges(
|
|
3027
3243
|
entityId,
|
|
3028
3244
|
item.sourceId,
|
|
@@ -3031,8 +3247,10 @@ var MaintenanceService = class {
|
|
|
3031
3247
|
manifest,
|
|
3032
3248
|
titleIndex,
|
|
3033
3249
|
tx,
|
|
3034
|
-
now
|
|
3250
|
+
now,
|
|
3251
|
+
resolveDrops
|
|
3035
3252
|
);
|
|
3253
|
+
for (const drop of resolveDrops) diagBuffer.push(edgeDropDiagnostic(drop, diagBase));
|
|
3036
3254
|
}
|
|
3037
3255
|
for (const task of validTasks) {
|
|
3038
3256
|
const id = generateId("task_");
|
|
@@ -3050,9 +3268,10 @@ var MaintenanceService = class {
|
|
|
3050
3268
|
await this.taskRepo.upsert(taskObj, tx);
|
|
3051
3269
|
}
|
|
3052
3270
|
});
|
|
3271
|
+
diagBuffer.flush(this.options);
|
|
3053
3272
|
await this.searchService.sync(entityId);
|
|
3054
3273
|
for (const fact of insertedFacts) {
|
|
3055
|
-
await this.embeddingService.embedFact(fact);
|
|
3274
|
+
await this.embeddingService.embedFact(fact, { operation: "librarian", trigger });
|
|
3056
3275
|
}
|
|
3057
3276
|
this.searchService.evictCache(entityId);
|
|
3058
3277
|
}
|
|
@@ -3066,6 +3285,8 @@ var MaintenanceService = class {
|
|
|
3066
3285
|
async doRunHeal(entityId, options) {
|
|
3067
3286
|
const promptOverride = options?.promptOverride;
|
|
3068
3287
|
const batchSize = options?.batchSize ?? HEAL_BATCH_SIZE;
|
|
3288
|
+
const trigger = options?.trigger ?? "call";
|
|
3289
|
+
const diagBase = { entityId, operation: "heal", trigger };
|
|
3069
3290
|
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
3070
3291
|
throw new Error("Invalid batchSize: must be an integer >= 1");
|
|
3071
3292
|
}
|
|
@@ -3101,6 +3322,7 @@ var MaintenanceService = class {
|
|
|
3101
3322
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
3102
3323
|
} catch (hookErr) {
|
|
3103
3324
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during heal orphan pass for ${factId}:`, hookErr);
|
|
3325
|
+
emitDiagnostic(this.options, { ...diagBase, code: "hook_failed", detail: { factId, reason: "on_embedding_persisted" } });
|
|
3104
3326
|
}
|
|
3105
3327
|
}
|
|
3106
3328
|
const healCandidates = await this.entryRepo.findHealCandidatesByEntityId(entityId, batchSize, recheckCutoff);
|
|
@@ -3173,22 +3395,38 @@ var MaintenanceService = class {
|
|
|
3173
3395
|
const safeDowngradedSet = /* @__PURE__ */ new Set();
|
|
3174
3396
|
const safeDeletedSet = /* @__PURE__ */ new Set();
|
|
3175
3397
|
const newFacts = [];
|
|
3398
|
+
const newFactItemIndexes = [];
|
|
3176
3399
|
for (const batchResult of outcome.results) {
|
|
3177
3400
|
const mutableIds = new Set(batchResult.batch.map((f) => f.id));
|
|
3178
3401
|
for (const id of batchResult.downgraded) if (mutableIds.has(id)) safeDowngradedSet.add(id);
|
|
3179
3402
|
for (const id of batchResult.deleted) if (mutableIds.has(id)) safeDeletedSet.add(id);
|
|
3180
|
-
newFacts.
|
|
3403
|
+
batchResult.newFacts.forEach((raw, itemIndex) => {
|
|
3404
|
+
newFacts.push(raw);
|
|
3405
|
+
newFactItemIndexes.push(itemIndex);
|
|
3406
|
+
});
|
|
3181
3407
|
}
|
|
3182
3408
|
const safeDowngraded = Array.from(safeDowngradedSet);
|
|
3183
3409
|
const safeDeleted = Array.from(safeDeletedSet);
|
|
3184
|
-
const
|
|
3410
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
3411
|
+
const validNewFacts = [];
|
|
3412
|
+
const validNewFactItemIndexes = [];
|
|
3413
|
+
newFacts.forEach((raw, k) => {
|
|
3414
|
+
const itemIndex = newFactItemIndexes[k];
|
|
3415
|
+
const valid = validateFact(raw);
|
|
3416
|
+
if (valid) {
|
|
3417
|
+
validNewFacts.push(valid);
|
|
3418
|
+
validNewFactItemIndexes.push(itemIndex);
|
|
3419
|
+
} else {
|
|
3420
|
+
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
|
|
3421
|
+
}
|
|
3422
|
+
});
|
|
3185
3423
|
const insertedFacts = [];
|
|
3186
3424
|
const uniqueDeletedFactIds = Array.from(new Set(safeDeleted));
|
|
3187
3425
|
await this.db.withTransactionAsync(async (tx) => {
|
|
3188
3426
|
await this.entryRepo.downgradeByIds(safeDowngraded, entityId, tx);
|
|
3189
3427
|
await this.entryRepo.softDeleteByIds(safeDeleted, entityId, tx);
|
|
3190
3428
|
const healFactsForDedupe = await this.entryRepo.findInferredTitlesByEntityId(entityId, tx);
|
|
3191
|
-
for (const fact of validNewFacts) {
|
|
3429
|
+
for (const [k, fact] of validNewFacts.entries()) {
|
|
3192
3430
|
const newTokens = titleTokens(fact.title);
|
|
3193
3431
|
let skip = false;
|
|
3194
3432
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
@@ -3202,7 +3440,10 @@ var MaintenanceService = class {
|
|
|
3202
3440
|
}
|
|
3203
3441
|
}
|
|
3204
3442
|
}
|
|
3205
|
-
if (skip)
|
|
3443
|
+
if (skip) {
|
|
3444
|
+
diagBuffer.push({ ...diagBase, code: "fact_deduplicated", detail: { itemIndex: validNewFactItemIndexes[k], reason: "fuzzy_title" } });
|
|
3445
|
+
continue;
|
|
3446
|
+
}
|
|
3206
3447
|
const id = generateId("fact_");
|
|
3207
3448
|
const factObj = {
|
|
3208
3449
|
id,
|
|
@@ -3237,16 +3478,21 @@ var MaintenanceService = class {
|
|
|
3237
3478
|
tx
|
|
3238
3479
|
);
|
|
3239
3480
|
});
|
|
3481
|
+
for (const { item, reason } of outcome.skipped) {
|
|
3482
|
+
diagBuffer.push({ ...diagBase, code: "heal_skipped", detail: { factId: item.id, reason } });
|
|
3483
|
+
}
|
|
3484
|
+
diagBuffer.flush(this.options);
|
|
3240
3485
|
await this.searchService.sync(entityId);
|
|
3241
3486
|
for (const factId of uniqueDeletedFactIds) {
|
|
3242
3487
|
try {
|
|
3243
3488
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, factId, null);
|
|
3244
3489
|
} catch (hookErr) {
|
|
3245
3490
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during heal for ${factId}:`, hookErr);
|
|
3491
|
+
emitDiagnostic(this.options, { ...diagBase, code: "hook_failed", detail: { factId, reason: "on_embedding_persisted" } });
|
|
3246
3492
|
}
|
|
3247
3493
|
}
|
|
3248
3494
|
for (const fact of insertedFacts) {
|
|
3249
|
-
await this.embeddingService.embedFact(fact);
|
|
3495
|
+
await this.embeddingService.embedFact(fact, { operation: "heal", trigger });
|
|
3250
3496
|
}
|
|
3251
3497
|
this.searchService.evictCache(entityId);
|
|
3252
3498
|
const skippedIds = new Set(outcome.skipped.map(({ item }) => item.id));
|
|
@@ -3386,6 +3632,8 @@ var MaintenanceService = class {
|
|
|
3386
3632
|
let failedValidation = 0;
|
|
3387
3633
|
let edgesAdded = 0;
|
|
3388
3634
|
let abortedOntologyOff = false;
|
|
3635
|
+
const diagBuffer = new DiagnosticBuffer();
|
|
3636
|
+
const diagBase = { entityId, operation: "ontologyBackfill", trigger: "call" };
|
|
3389
3637
|
await this.db.withTransactionAsync(async (tx) => {
|
|
3390
3638
|
let { mode: txMode, manifest } = await ontologyService.getEffectiveState(entityId, tx);
|
|
3391
3639
|
if (txMode === "off") {
|
|
@@ -3410,6 +3658,7 @@ var MaintenanceService = class {
|
|
|
3410
3658
|
continue;
|
|
3411
3659
|
}
|
|
3412
3660
|
applied.add(fact.id);
|
|
3661
|
+
const validationDrops = [];
|
|
3413
3662
|
const normalized = ontologyService.validateAndNormalizeFact(
|
|
3414
3663
|
{
|
|
3415
3664
|
title: fact.title,
|
|
@@ -3420,8 +3669,11 @@ var MaintenanceService = class {
|
|
|
3420
3669
|
edges: classification.edges
|
|
3421
3670
|
},
|
|
3422
3671
|
manifest,
|
|
3423
|
-
{ strict: false }
|
|
3672
|
+
{ strict: false, drops: validationDrops }
|
|
3424
3673
|
);
|
|
3674
|
+
for (const drop of validationDrops) {
|
|
3675
|
+
diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: fact.id }));
|
|
3676
|
+
}
|
|
3425
3677
|
if (!normalized.okf_type) {
|
|
3426
3678
|
failedValidation++;
|
|
3427
3679
|
continue;
|
|
@@ -3435,7 +3687,8 @@ var MaintenanceService = class {
|
|
|
3435
3687
|
}
|
|
3436
3688
|
}
|
|
3437
3689
|
for (const item of pendingEdges) {
|
|
3438
|
-
|
|
3690
|
+
const resolveDrops = [];
|
|
3691
|
+
const added = await ontologyService.resolveAndPersistEdges(
|
|
3439
3692
|
entityId,
|
|
3440
3693
|
item.sourceId,
|
|
3441
3694
|
item.sourceType,
|
|
@@ -3443,11 +3696,17 @@ var MaintenanceService = class {
|
|
|
3443
3696
|
manifest,
|
|
3444
3697
|
titleIndex,
|
|
3445
3698
|
tx,
|
|
3446
|
-
now
|
|
3699
|
+
now,
|
|
3700
|
+
resolveDrops
|
|
3447
3701
|
);
|
|
3702
|
+
edgesAdded += added;
|
|
3703
|
+
for (const drop of resolveDrops) {
|
|
3704
|
+
diagBuffer.push(edgeDropDiagnostic(drop, diagBase));
|
|
3705
|
+
}
|
|
3448
3706
|
}
|
|
3449
3707
|
await this.entryRepo.markOntologyChecked(batch.map((f) => f.id), entityId, now, tx);
|
|
3450
3708
|
});
|
|
3709
|
+
diagBuffer.flush(this.options);
|
|
3451
3710
|
return { typed, failedValidation, edgesAdded, abortedOntologyOff };
|
|
3452
3711
|
}
|
|
3453
3712
|
_validatePruneDuration(value, name) {
|
|
@@ -3849,7 +4108,7 @@ var ImportExportService = class {
|
|
|
3849
4108
|
title: clipped?.title ?? fact.title,
|
|
3850
4109
|
body: clipped?.body ?? fact.body,
|
|
3851
4110
|
tags: Array.isArray(fact.tags) || typeof fact.tags === "string" ? fact.tags : []
|
|
3852
|
-
});
|
|
4111
|
+
}, { operation: "importDump", trigger: "call" });
|
|
3853
4112
|
if (!embedded) {
|
|
3854
4113
|
await this.embeddingService.notifyEmbeddingPersisted(entityId, fact.id, null);
|
|
3855
4114
|
}
|
|
@@ -3874,6 +4133,11 @@ var ImportExportService = class {
|
|
|
3874
4133
|
`[WikiMemory] onEmbeddingPersisted hook failed for preserved-blob fact ${fact.id}:`,
|
|
3875
4134
|
hookErr
|
|
3876
4135
|
);
|
|
4136
|
+
this.embeddingService.reportHookFailed(
|
|
4137
|
+
{ operation: "importDump", trigger: "call" },
|
|
4138
|
+
entityId,
|
|
4139
|
+
fact.id
|
|
4140
|
+
);
|
|
3877
4141
|
}
|
|
3878
4142
|
}
|
|
3879
4143
|
}
|
|
@@ -3890,6 +4154,11 @@ var ImportExportService = class {
|
|
|
3890
4154
|
`[WikiMemory] onEmbeddingPersisted(vector=null) hook failed for soft-deleted fact ${factId}:`,
|
|
3891
4155
|
hookErr
|
|
3892
4156
|
);
|
|
4157
|
+
this.embeddingService.reportHookFailed(
|
|
4158
|
+
{ operation: "importDump", trigger: "call" },
|
|
4159
|
+
entityId,
|
|
4160
|
+
factId
|
|
4161
|
+
);
|
|
3893
4162
|
}
|
|
3894
4163
|
}
|
|
3895
4164
|
}
|
|
@@ -4052,7 +4321,7 @@ var EmbeddingService = class {
|
|
|
4052
4321
|
});
|
|
4053
4322
|
}
|
|
4054
4323
|
}
|
|
4055
|
-
async tryEmbedFact(fact) {
|
|
4324
|
+
async tryEmbedFact(fact, ctx) {
|
|
4056
4325
|
const embedFn = this.options.llmProvider.embed;
|
|
4057
4326
|
if (typeof embedFn !== "function") return { ok: false, kind: "no_provider" };
|
|
4058
4327
|
let tagsStr;
|
|
@@ -4074,6 +4343,7 @@ var EmbeddingService = class {
|
|
|
4074
4343
|
const vector = await embedFn(text);
|
|
4075
4344
|
if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
|
|
4076
4345
|
console.warn(`[WikiMemory] embedFact: embed() returned an invalid vector for ${fact.id}; skipping.`);
|
|
4346
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "invalid_vector");
|
|
4077
4347
|
await this.markFailure(fact.id, "invalid_vector");
|
|
4078
4348
|
return { ok: false, kind: "invalid_vector" };
|
|
4079
4349
|
}
|
|
@@ -4087,11 +4357,13 @@ var EmbeddingService = class {
|
|
|
4087
4357
|
}
|
|
4088
4358
|
if (hasNonFinite) {
|
|
4089
4359
|
console.warn(`[WikiMemory] embedFact: embed() returned values that overflow float32 for ${fact.id}; skipping.`);
|
|
4360
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "float32_overflow");
|
|
4090
4361
|
await this.markFailure(fact.id, "float32_overflow");
|
|
4091
4362
|
return { ok: false, kind: "float32_overflow" };
|
|
4092
4363
|
}
|
|
4093
4364
|
} catch (err) {
|
|
4094
4365
|
console.warn(`[WikiMemory] embedFact failed for ${fact.id}:`, err);
|
|
4366
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "embed_threw");
|
|
4095
4367
|
await this.markFailure(fact.id, "provider_error");
|
|
4096
4368
|
return { ok: false, kind: "provider_error" };
|
|
4097
4369
|
}
|
|
@@ -4101,15 +4373,31 @@ var EmbeddingService = class {
|
|
|
4101
4373
|
await this.entryRepo.updateEmbeddingBlob(fact.id, blob);
|
|
4102
4374
|
} catch (err) {
|
|
4103
4375
|
console.warn(`[WikiMemory] embedFact: persisting embedding failed for ${fact.id}:`, err);
|
|
4376
|
+
this.reportEmbed(ctx, fact, "embedding_failed", "persist_failed");
|
|
4104
4377
|
return { ok: false, kind: "storage_error" };
|
|
4105
4378
|
}
|
|
4106
4379
|
try {
|
|
4107
4380
|
await this.notifyEmbeddingPersisted(fact.entity_id, fact.id, float32Vector);
|
|
4108
4381
|
} catch (hookErr) {
|
|
4109
4382
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed for ${fact.id}:`, hookErr);
|
|
4383
|
+
this.reportEmbed(ctx, fact, "hook_failed", "on_embedding_persisted");
|
|
4110
4384
|
}
|
|
4111
4385
|
return { ok: true, dimension: float32Vector.length };
|
|
4112
4386
|
}
|
|
4387
|
+
reportEmbed(ctx, fact, code, reason) {
|
|
4388
|
+
if (!ctx) return;
|
|
4389
|
+
emitDiagnostic(this.options, {
|
|
4390
|
+
code,
|
|
4391
|
+
operation: ctx.operation,
|
|
4392
|
+
trigger: ctx.trigger,
|
|
4393
|
+
entityId: fact.entity_id,
|
|
4394
|
+
detail: { factId: fact.id, reason }
|
|
4395
|
+
});
|
|
4396
|
+
}
|
|
4397
|
+
/** For callers that invoke `notifyEmbeddingPersisted` directly and catch its failure. */
|
|
4398
|
+
reportHookFailed(ctx, entityId, factId) {
|
|
4399
|
+
this.reportEmbed(ctx, { id: factId, entity_id: entityId }, "hook_failed", "on_embedding_persisted");
|
|
4400
|
+
}
|
|
4113
4401
|
/** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
|
|
4114
4402
|
async markFailure(id, kind) {
|
|
4115
4403
|
try {
|
|
@@ -4123,8 +4411,8 @@ var EmbeddingService = class {
|
|
|
4123
4411
|
* ImportExportService branch on `if (!embedded)`; a discriminated result
|
|
4124
4412
|
* object is always truthy and would silently disable that branch.
|
|
4125
4413
|
*/
|
|
4126
|
-
async embedFact(fact) {
|
|
4127
|
-
const result = await this.tryEmbedFact(fact);
|
|
4414
|
+
async embedFact(fact, ctx) {
|
|
4415
|
+
const result = await this.tryEmbedFact(fact, ctx);
|
|
4128
4416
|
return result.ok;
|
|
4129
4417
|
}
|
|
4130
4418
|
async notifyEmbeddingPersisted(entityId, factId, vector) {
|
|
@@ -4924,7 +5212,11 @@ var WriteService = class {
|
|
|
4924
5212
|
if (shouldRunLibrarian) {
|
|
4925
5213
|
try {
|
|
4926
5214
|
this.jobManager.acquireLock("librarian", entityId);
|
|
4927
|
-
|
|
5215
|
+
const stage = { job: "librarian" };
|
|
5216
|
+
this.runLibrarianThenMaybeHeal(entityId, librarianCount, prevMemoryCheckpoint, stage).catch((err) => {
|
|
5217
|
+
console.error(err);
|
|
5218
|
+
this.reportBackgroundFailure(entityId, stage.job);
|
|
5219
|
+
}).finally(() => {
|
|
4928
5220
|
this.jobManager.releaseLock("librarian", entityId);
|
|
4929
5221
|
});
|
|
4930
5222
|
} catch (e) {
|
|
@@ -4932,17 +5224,21 @@ var WriteService = class {
|
|
|
4932
5224
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevMemoryCheckpoint }, this.db);
|
|
4933
5225
|
}
|
|
4934
5226
|
} else if (!this.jobManager.isBlocked("librarian", entityId)) {
|
|
4935
|
-
this.maybeRunHeal(entityId, eventCount).catch(
|
|
5227
|
+
this.maybeRunHeal(entityId, eventCount).catch((err) => {
|
|
5228
|
+
console.error(err);
|
|
5229
|
+
this.reportBackgroundFailure(entityId, "heal");
|
|
5230
|
+
});
|
|
4936
5231
|
}
|
|
4937
5232
|
}
|
|
4938
|
-
async runLibrarianThenMaybeHeal(entityId, currentEventCount, prevCheckpoint) {
|
|
5233
|
+
async runLibrarianThenMaybeHeal(entityId, currentEventCount, prevCheckpoint, stage) {
|
|
4939
5234
|
try {
|
|
4940
|
-
await this.maintenanceService.doRunLibrarian(entityId);
|
|
5235
|
+
await this.maintenanceService.doRunLibrarian(entityId, void 0, "auto");
|
|
4941
5236
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: currentEventCount }, this.db);
|
|
4942
5237
|
} catch (e) {
|
|
4943
5238
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevCheckpoint }, this.db);
|
|
4944
5239
|
throw e;
|
|
4945
5240
|
}
|
|
5241
|
+
stage.job = "heal";
|
|
4946
5242
|
await this.maybeRunHeal(entityId, currentEventCount);
|
|
4947
5243
|
}
|
|
4948
5244
|
/**
|
|
@@ -4958,7 +5254,7 @@ var WriteService = class {
|
|
|
4958
5254
|
const shouldRunHeal = currentEventCount - healCheckpoint >= autoHealThreshold;
|
|
4959
5255
|
if (shouldRunHeal && this.jobManager.tryAcquireAutoHealLock(entityId)) {
|
|
4960
5256
|
try {
|
|
4961
|
-
const result = await this.maintenanceService.doRunHeal(entityId);
|
|
5257
|
+
const result = await this.maintenanceService.doRunHeal(entityId, { trigger: "auto" });
|
|
4962
5258
|
if (result.remaining === 0) {
|
|
4963
5259
|
await this.metadataRepo.updateCheckpoint(entityId, { heal: currentEventCount }, this.db);
|
|
4964
5260
|
}
|
|
@@ -4967,8 +5263,17 @@ var WriteService = class {
|
|
|
4967
5263
|
}
|
|
4968
5264
|
}
|
|
4969
5265
|
}
|
|
5266
|
+
reportBackgroundFailure(entityId, job) {
|
|
5267
|
+
emitDiagnostic(this.options, {
|
|
5268
|
+
code: "background_job_failed",
|
|
5269
|
+
operation: job,
|
|
5270
|
+
trigger: "auto",
|
|
5271
|
+
entityId,
|
|
5272
|
+
detail: { reason: "unhandled_rejection" }
|
|
5273
|
+
});
|
|
5274
|
+
}
|
|
4970
5275
|
};
|
|
4971
5276
|
|
|
4972
|
-
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, EmbeddingService, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
4973
|
-
//# sourceMappingURL=chunk-
|
|
4974
|
-
//# sourceMappingURL=chunk-
|
|
5277
|
+
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, DiagnosticBuffer, EMBED_CHARS_CEILING, EmbeddingService, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
5278
|
+
//# sourceMappingURL=chunk-IAYO63DQ.mjs.map
|
|
5279
|
+
//# sourceMappingURL=chunk-IAYO63DQ.mjs.map
|