@equationalapplications/core-llm-wiki 7.7.3 → 7.7.5
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 +3 -1
- package/dist/{chunk-NWODQRSM.mjs → chunk-JV2OQ5CM.mjs} +50 -28
- package/dist/chunk-JV2OQ5CM.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +48 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{testing-A4Sofxyo.d.mts → testing-CH_Kk34-.d.mts} +11 -0
- package/dist/{testing-A4Sofxyo.d.ts → testing-CH_Kk34-.d.ts} +11 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +48 -26
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-NWODQRSM.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -361,7 +361,7 @@ new WikiMemory(db, {
|
|
|
361
361
|
- Instructions, the ontology manifest, existing facts and identifiers never count, so a model cannot ground a claim by quoting them.
|
|
362
362
|
- **The check.** Both sides are normalized with NFKC, whitespace runs collapse to one space, and matching is case-sensitive. A fact with more than 10 quotes, or any quote not found, fails.
|
|
363
363
|
- **Diagnostics.** `grounding_missing` (reasons `no_evidence`, `evidence_too_short`) and `grounding_failed` (reasons `quote_not_found`, `too_many_quotes`), one per fact, with the new fact's `factId`. Quotes are never included.
|
|
364
|
-
- **Duplicate titles in one ingest.** When chunks yield facts with the same title, ingest keeps one: a grounded fact beats one with missing or failed evidence, and on a tie the first one wins. The others are reported as `fact_deduplicated`. A fact already stored for the same `sourceRef` is not replaced by a later partial ingest.
|
|
364
|
+
- **Duplicate titles in one ingest.** When chunks yield facts with the same title, ingest keeps one: a grounded fact beats one with missing or failed evidence, and on a tie the first one wins. The others are reported as `fact_deduplicated`. A fact already stored for the same `sourceRef` is not replaced by a later partial ingest, and that skip is reported the same way. Either way the diagnostic carries `{ sourceRef, chunkIndex, itemIndex }` for the skipped fact, whether or not grounding is enabled.
|
|
365
365
|
- **`upsertGraph`** nodes are host-supplied and never grounded.
|
|
366
366
|
- **Librarian and heal** synthesize across events, so their pass rates are unknown. Measure them on your own event log before opting them in.
|
|
367
367
|
- Evidence quotes are not stored.
|
|
@@ -1217,6 +1217,8 @@ await wikiMemory.ingestDocument(
|
|
|
1217
1217
|
- `'throw'`: Pre-check before any LLM call; throw `WikiDuplicateHashError` (carries the canonical `sourceRef`).
|
|
1218
1218
|
- The guard only considers **live** references — soft-deleted refs do not trigger it in any mode.
|
|
1219
1219
|
|
|
1220
|
+
When the pre-check passes but a concurrent writer claims the hash before the write commits, the transaction rolls back after the LLM extraction has already run. You still receive the diagnostics that describe that extraction — `ingest_chunk_failed`, `fact_rejected` and `fact_deduplicated` — in every mode, and also in the rarer case where the winning writer has itself rolled back by the time core looks, so the call surfaces the raw database error instead of `WikiDuplicateHashError`. Diagnostics tied to the rolled-back write (`grounding_*`, `edge_dropped`) are dropped, because the `factId`s they carry name rows that were never committed.
|
|
1221
|
+
|
|
1220
1222
|
## Batch Change Detection
|
|
1221
1223
|
|
|
1222
1224
|
Check multiple documents for changes in one call:
|
|
@@ -2018,6 +2018,23 @@ var DiagnosticBuffer = class {
|
|
|
2018
2018
|
this.items = [];
|
|
2019
2019
|
for (const input of pending) emitDiagnostic(options, input);
|
|
2020
2020
|
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Emit only the buffered diagnostics whose code is in `codes` and discard
|
|
2023
|
+
* the rest. The buffer is empty afterwards either way.
|
|
2024
|
+
*
|
|
2025
|
+
* For an operation that did real work before its write was rolled back:
|
|
2026
|
+
* some buffered diagnostics describe that work and are still true, while
|
|
2027
|
+
* others describe rows that no longer exist (anything carrying a `factId`
|
|
2028
|
+
* minted inside the aborted transaction). `flush` would emit both and
|
|
2029
|
+
* `discard` would drop both; this emits the first set only.
|
|
2030
|
+
*/
|
|
2031
|
+
flushOnly(options, codes) {
|
|
2032
|
+
const pending = this.items;
|
|
2033
|
+
this.items = [];
|
|
2034
|
+
for (const input of pending) {
|
|
2035
|
+
if (codes.includes(input.code)) emitDiagnostic(options, input);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2021
2038
|
discard() {
|
|
2022
2039
|
this.items = [];
|
|
2023
2040
|
}
|
|
@@ -2034,6 +2051,16 @@ function edgeDropDiagnostic(drop, ctx) {
|
|
|
2034
2051
|
}
|
|
2035
2052
|
|
|
2036
2053
|
// src/services/IngestionService.ts
|
|
2054
|
+
var LLM_PASS_DIAGNOSTIC_CODES = ["ingest_chunk_failed", "fact_rejected", "fact_deduplicated"];
|
|
2055
|
+
function dedupDiagnostic(entityId, sourceRef, locator) {
|
|
2056
|
+
return {
|
|
2057
|
+
entityId,
|
|
2058
|
+
operation: "ingest",
|
|
2059
|
+
trigger: "call",
|
|
2060
|
+
code: "fact_deduplicated",
|
|
2061
|
+
detail: locator ? { sourceRef, chunkIndex: locator.chunkIndex, itemIndex: locator.itemIndex, reason: "exact_title" } : { sourceRef, reason: "exact_title" }
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2037
2064
|
function zeroChunkResult(duplicateOf) {
|
|
2038
2065
|
const result = {
|
|
2039
2066
|
truncated: false,
|
|
@@ -2170,11 +2197,7 @@ var IngestionService = class {
|
|
|
2170
2197
|
const diagBuffer = new DiagnosticBuffer();
|
|
2171
2198
|
const diagBase = { entityId, operation: "ingest", trigger: "call" };
|
|
2172
2199
|
const pushDeduplicated = (chunkIndex, itemIndex) => {
|
|
2173
|
-
diagBuffer.push({
|
|
2174
|
-
...diagBase,
|
|
2175
|
-
code: "fact_deduplicated",
|
|
2176
|
-
detail: { sourceRef, chunkIndex, itemIndex, reason: "exact_title" }
|
|
2177
|
-
});
|
|
2200
|
+
diagBuffer.push(dedupDiagnostic(entityId, sourceRef, { chunkIndex, itemIndex }));
|
|
2178
2201
|
};
|
|
2179
2202
|
for (const [chunkIndex, slot] of chunkResults.entries()) {
|
|
2180
2203
|
if (slot.status === "failed") {
|
|
@@ -2201,7 +2224,7 @@ var IngestionService = class {
|
|
|
2201
2224
|
});
|
|
2202
2225
|
}
|
|
2203
2226
|
const orderedChunkFacts = [];
|
|
2204
|
-
const
|
|
2227
|
+
const factLedger = /* @__PURE__ */ new Map();
|
|
2205
2228
|
for (const [chunkIndex, slot] of chunkResults.entries()) {
|
|
2206
2229
|
if (slot.status === "failed") continue;
|
|
2207
2230
|
const keptFacts = [];
|
|
@@ -2209,7 +2232,11 @@ var IngestionService = class {
|
|
|
2209
2232
|
const winner = winners.get(normalizeTitleKey(fact.title));
|
|
2210
2233
|
if (winner?.chunkIndex !== chunkIndex || winner.k !== k) return;
|
|
2211
2234
|
keptFacts.push(fact);
|
|
2212
|
-
|
|
2235
|
+
factLedger.set(fact, {
|
|
2236
|
+
verdict: ingestGrounding ? slot.verdicts[k] : null,
|
|
2237
|
+
chunkIndex,
|
|
2238
|
+
itemIndex: winner.itemIndex
|
|
2239
|
+
});
|
|
2213
2240
|
});
|
|
2214
2241
|
orderedChunkFacts.push({ facts: keptFacts, ontology_updates: slot.ontology_updates });
|
|
2215
2242
|
}
|
|
@@ -2239,7 +2266,7 @@ var IngestionService = class {
|
|
|
2239
2266
|
try {
|
|
2240
2267
|
if (failedChunks === 0) {
|
|
2241
2268
|
const fullResult = await this.db.withTransactionAsync(async (tx) => {
|
|
2242
|
-
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer,
|
|
2269
|
+
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, factLedger);
|
|
2243
2270
|
});
|
|
2244
2271
|
deletedSourceFactIds.push(...fullResult.deletedSourceFactIds);
|
|
2245
2272
|
insertedFacts.push(...fullResult.insertedFacts);
|
|
@@ -2247,13 +2274,14 @@ var IngestionService = class {
|
|
|
2247
2274
|
const partialResult = await this.db.withTransactionAsync(async (tx) => {
|
|
2248
2275
|
const flat = [];
|
|
2249
2276
|
for (const slot of orderedChunkFacts) flat.push(...slot.facts);
|
|
2250
|
-
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer,
|
|
2277
|
+
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer, factLedger);
|
|
2251
2278
|
});
|
|
2252
2279
|
insertedFacts.push(...partialResult.insertedDescriptors);
|
|
2253
2280
|
}
|
|
2254
2281
|
} catch (err) {
|
|
2255
2282
|
const sqliteCode = err instanceof WikiTransactionError ? err.sqliteErrorCode : extractSqliteCode(err);
|
|
2256
2283
|
if (sqliteCode !== "SQLITE_CONSTRAINT_UNIQUE") throw err;
|
|
2284
|
+
diagBuffer.flushOnly(this.options, LLM_PASS_DIAGNOSTIC_CODES);
|
|
2257
2285
|
const canonical = await this.sourceRefIndexRepo.findActiveByEntityAndHash(entityId, sourceHash);
|
|
2258
2286
|
if (canonical === null) throw err;
|
|
2259
2287
|
if (onDuplicateHash === "throw" || onDuplicateHash === "ingest") {
|
|
@@ -2481,7 +2509,7 @@ var IngestionService = class {
|
|
|
2481
2509
|
* On the partial path the caller never invokes this method; the empty
|
|
2482
2510
|
* arrays stay empty.
|
|
2483
2511
|
*/
|
|
2484
|
-
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer,
|
|
2512
|
+
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, factLedger = /* @__PURE__ */ new Map()) {
|
|
2485
2513
|
const deletedSourceFactIds = [];
|
|
2486
2514
|
const insertedFacts = [];
|
|
2487
2515
|
const nodeTrust = /* @__PURE__ */ new Map();
|
|
@@ -2528,9 +2556,9 @@ var IngestionService = class {
|
|
|
2528
2556
|
tags: fact.tags,
|
|
2529
2557
|
confidence: fact.confidence
|
|
2530
2558
|
});
|
|
2531
|
-
const
|
|
2532
|
-
if (
|
|
2533
|
-
const outcome = groundingOutcome(
|
|
2559
|
+
const locator = factLedger.get(fact);
|
|
2560
|
+
if (locator?.verdict) {
|
|
2561
|
+
const outcome = groundingOutcome(locator.verdict, now);
|
|
2534
2562
|
nodeTrust.set(id, outcome.trust);
|
|
2535
2563
|
if (outcome.diagnostic) {
|
|
2536
2564
|
diagBuffer.push({
|
|
@@ -2538,7 +2566,7 @@ var IngestionService = class {
|
|
|
2538
2566
|
operation: "ingest",
|
|
2539
2567
|
trigger: "call",
|
|
2540
2568
|
code: outcome.diagnostic.code,
|
|
2541
|
-
detail: { factId: id, sourceRef, chunkIndex:
|
|
2569
|
+
detail: { factId: id, sourceRef, chunkIndex: locator.chunkIndex, itemIndex: locator.itemIndex, reason: outcome.diagnostic.reason }
|
|
2542
2570
|
});
|
|
2543
2571
|
}
|
|
2544
2572
|
}
|
|
@@ -2597,7 +2625,7 @@ var IngestionService = class {
|
|
|
2597
2625
|
* Runs INSIDE the caller's `tx`. Does not open a nested transaction.
|
|
2598
2626
|
* Returns `{ inserted, skippedDuplicate }` for observability.
|
|
2599
2627
|
*/
|
|
2600
|
-
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer,
|
|
2628
|
+
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer, factLedger = /* @__PURE__ */ new Map()) {
|
|
2601
2629
|
const liveIds = await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false);
|
|
2602
2630
|
const liveFacts = liveIds.length === 0 ? [] : await this.entryRepo.findByIds(liveIds, void 0, tx);
|
|
2603
2631
|
const liveTitles = new Set(liveFacts.map((f) => normalizeTitleKey(f.title)));
|
|
@@ -2607,28 +2635,22 @@ var IngestionService = class {
|
|
|
2607
2635
|
const insertedDescriptors = [];
|
|
2608
2636
|
for (const fact of dedupedFacts) {
|
|
2609
2637
|
const normalizedTitle = normalizeTitleKey(fact.title);
|
|
2638
|
+
const locator = factLedger.get(fact);
|
|
2610
2639
|
if (liveTitles.has(normalizedTitle)) {
|
|
2611
|
-
diagBuffer.push(
|
|
2612
|
-
entityId,
|
|
2613
|
-
operation: "ingest",
|
|
2614
|
-
trigger: "call",
|
|
2615
|
-
code: "fact_deduplicated",
|
|
2616
|
-
detail: { sourceRef, reason: "exact_title" }
|
|
2617
|
-
});
|
|
2640
|
+
diagBuffer.push(dedupDiagnostic(entityId, sourceRef, locator));
|
|
2618
2641
|
skippedDuplicate++;
|
|
2619
2642
|
continue;
|
|
2620
2643
|
}
|
|
2621
2644
|
liveTitles.add(normalizedTitle);
|
|
2622
2645
|
const id = generateId("fact_");
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2625
|
-
if (grounding && outcome?.diagnostic) {
|
|
2646
|
+
const outcome = locator?.verdict ? groundingOutcome(locator.verdict, now) : null;
|
|
2647
|
+
if (locator?.verdict && outcome?.diagnostic) {
|
|
2626
2648
|
diagBuffer.push({
|
|
2627
2649
|
entityId,
|
|
2628
2650
|
operation: "ingest",
|
|
2629
2651
|
trigger: "call",
|
|
2630
2652
|
code: outcome.diagnostic.code,
|
|
2631
|
-
detail: { factId: id, sourceRef, chunkIndex:
|
|
2653
|
+
detail: { factId: id, sourceRef, chunkIndex: locator.chunkIndex, itemIndex: locator.itemIndex, reason: outcome.diagnostic.reason }
|
|
2632
2654
|
});
|
|
2633
2655
|
}
|
|
2634
2656
|
const wikiFact = {
|
|
@@ -5641,5 +5663,5 @@ var WriteService = class {
|
|
|
5641
5663
|
};
|
|
5642
5664
|
|
|
5643
5665
|
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, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, edgeTripleAllowed, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveGrounding, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
5644
|
-
//# sourceMappingURL=chunk-
|
|
5645
|
-
//# sourceMappingURL=chunk-
|
|
5666
|
+
//# sourceMappingURL=chunk-JV2OQ5CM.mjs.map
|
|
5667
|
+
//# sourceMappingURL=chunk-JV2OQ5CM.mjs.map
|