@equationalapplications/core-llm-wiki 7.7.0 → 7.7.1

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 CHANGED
@@ -361,6 +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
365
  - **`upsertGraph`** nodes are host-supplied and never grounded.
365
366
  - **Librarian and heal** synthesize across events, so their pass rates are unknown. Measure them on your own event log before opting them in.
366
367
  - Evidence quotes are not stored.
@@ -2166,11 +2166,16 @@ var IngestionService = class {
2166
2166
  let ingestedChunks = 0;
2167
2167
  let failedChunks = 0;
2168
2168
  const failures = [];
2169
- const seen = /* @__PURE__ */ new Set();
2170
- const orderedChunkFacts = [];
2171
- const groundingLedger = /* @__PURE__ */ new Map();
2169
+ const winners = /* @__PURE__ */ new Map();
2172
2170
  const diagBuffer = new DiagnosticBuffer();
2173
2171
  const diagBase = { entityId, operation: "ingest", trigger: "call" };
2172
+ const pushDeduplicated = (chunkIndex, itemIndex) => {
2173
+ diagBuffer.push({
2174
+ ...diagBase,
2175
+ code: "fact_deduplicated",
2176
+ detail: { sourceRef, chunkIndex, itemIndex, reason: "exact_title" }
2177
+ });
2178
+ };
2174
2179
  for (const [chunkIndex, slot] of chunkResults.entries()) {
2175
2180
  if (slot.status === "failed") {
2176
2181
  failedChunks++;
@@ -2181,22 +2186,32 @@ var IngestionService = class {
2181
2186
  for (const r of slot.rejected) {
2182
2187
  diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { sourceRef, chunkIndex, itemIndex: r.itemIndex, reason: r.reason } });
2183
2188
  }
2184
- const dedupedFacts = [];
2185
2189
  slot.facts.forEach((fact, k) => {
2186
- const normalizedTitle = normalizeTitleKey(fact.title);
2187
- if (!seen.has(normalizedTitle)) {
2188
- seen.add(normalizedTitle);
2189
- dedupedFacts.push(fact);
2190
- if (ingestGrounding) groundingLedger.set(fact, { verdict: slot.verdicts[k], chunkIndex, itemIndex: slot.itemIndexes[k] });
2190
+ const key = normalizeTitleKey(fact.title);
2191
+ const candidate = { chunkIndex, k, itemIndex: slot.itemIndexes[k], rank: slot.verdicts[k]?.status === "grounded" ? 1 : 0 };
2192
+ const current = winners.get(key);
2193
+ if (!current) {
2194
+ winners.set(key, candidate);
2195
+ } else if (candidate.rank > current.rank) {
2196
+ pushDeduplicated(current.chunkIndex, current.itemIndex);
2197
+ winners.set(key, candidate);
2191
2198
  } else {
2192
- diagBuffer.push({
2193
- ...diagBase,
2194
- code: "fact_deduplicated",
2195
- detail: { sourceRef, chunkIndex, itemIndex: slot.itemIndexes[k], reason: "exact_title" }
2196
- });
2199
+ pushDeduplicated(candidate.chunkIndex, candidate.itemIndex);
2197
2200
  }
2198
2201
  });
2199
- orderedChunkFacts.push({ facts: dedupedFacts, ontology_updates: slot.ontology_updates });
2202
+ }
2203
+ const orderedChunkFacts = [];
2204
+ const groundingLedger = /* @__PURE__ */ new Map();
2205
+ for (const [chunkIndex, slot] of chunkResults.entries()) {
2206
+ if (slot.status === "failed") continue;
2207
+ const keptFacts = [];
2208
+ slot.facts.forEach((fact, k) => {
2209
+ const winner = winners.get(normalizeTitleKey(fact.title));
2210
+ if (winner?.chunkIndex !== chunkIndex || winner.k !== k) return;
2211
+ keptFacts.push(fact);
2212
+ if (ingestGrounding) groundingLedger.set(fact, { verdict: slot.verdicts[k], chunkIndex, itemIndex: winner.itemIndex });
2213
+ });
2214
+ orderedChunkFacts.push({ facts: keptFacts, ontology_updates: slot.ontology_updates });
2200
2215
  }
2201
2216
  for (const reason of ["parse", "llm"]) {
2202
2217
  const ofReason = failures.filter((f) => f.source === reason);
@@ -5626,5 +5641,5 @@ var WriteService = class {
5626
5641
  };
5627
5642
 
5628
5643
  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 };
5629
- //# sourceMappingURL=chunk-VOSMYISQ.mjs.map
5630
- //# sourceMappingURL=chunk-VOSMYISQ.mjs.map
5644
+ //# sourceMappingURL=chunk-NWODQRSM.mjs.map
5645
+ //# sourceMappingURL=chunk-NWODQRSM.mjs.map