@equationalapplications/core-llm-wiki 7.5.0 → 7.6.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 +30 -0
- package/dist/{chunk-3P7FAKJA.mjs → chunk-YBWE26XR.mjs} +224 -41
- package/dist/chunk-YBWE26XR.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +225 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -5
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-ClpOUiLI.d.mts → testing-d1SrpDvg.d.mts} +52 -3
- package/dist/{testing-ClpOUiLI.d.ts → testing-d1SrpDvg.d.ts} +52 -3
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +221 -38
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-3P7FAKJA.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-
|
|
2
|
-
export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE,
|
|
1
|
+
import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-d1SrpDvg.mjs';
|
|
2
|
+
export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, q as GroundingConfig, H as HEAL_BATCH_SIZE, r as HEAL_RECHECK_MS, s as HOOK_TIMEOUT_MARKER, t as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, u as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, v as ONTOLOGY_BACKFILL_RECHECK_MS, w as OntologyBackfillResult, x as OntologyConfig, y as OntologyEdgeType, z as OntologyManifest, A as OntologyMode, B as OntologyNodeType, J as OntologyPromptContext, K as OntologyUpdates, P as PromptOverrides, N as PromptService, Q as PrunePartialFailureError, T as ReembedResult, V as VectorRanker, U as VectorRankerFallback, X as VectorRankerRankArgs, Y as VectorRankerSemanticResult, Z as WikiBusyError, _ as WikiBusyOperation, $ as WikiCheckpoint, a0 as WikiConfig, a1 as WikiDiagnostic, a2 as WikiDiagnosticCode, a3 as WikiDiagnosticDetail, a4 as WikiDiagnosticOperation, a5 as WikiDiagnosticSeverity, a6 as WikiDiagnosticTrigger, a7 as WikiDraftNotFound, a8 as WikiDuplicateHashError, a9 as WikiEdge, aa as WikiEvent, ab as WikiFact, ac as WikiGraphNodeOwnershipConflict, ad as WikiIngestEmptyError, ae as WikiInvalidReadOptions, af as WikiMemoryTestAccess, ag as WikiOutboxEvent, ah as WikiParseError, ai as WikiSourceRefHashCollision, aj as WikiStrictOntologyViolation, ak as WikiTask, al as WikiTransactionError, am as validateManifest } from './testing-d1SrpDvg.mjs';
|
|
3
3
|
import { OkfFile } from '@equationalapplications/core-okf';
|
|
4
4
|
import 'minisearch';
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-
|
|
2
|
-
export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE,
|
|
1
|
+
import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-d1SrpDvg.js';
|
|
2
|
+
export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, q as GroundingConfig, H as HEAL_BATCH_SIZE, r as HEAL_RECHECK_MS, s as HOOK_TIMEOUT_MARKER, t as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, u as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, v as ONTOLOGY_BACKFILL_RECHECK_MS, w as OntologyBackfillResult, x as OntologyConfig, y as OntologyEdgeType, z as OntologyManifest, A as OntologyMode, B as OntologyNodeType, J as OntologyPromptContext, K as OntologyUpdates, P as PromptOverrides, N as PromptService, Q as PrunePartialFailureError, T as ReembedResult, V as VectorRanker, U as VectorRankerFallback, X as VectorRankerRankArgs, Y as VectorRankerSemanticResult, Z as WikiBusyError, _ as WikiBusyOperation, $ as WikiCheckpoint, a0 as WikiConfig, a1 as WikiDiagnostic, a2 as WikiDiagnosticCode, a3 as WikiDiagnosticDetail, a4 as WikiDiagnosticOperation, a5 as WikiDiagnosticSeverity, a6 as WikiDiagnosticTrigger, a7 as WikiDraftNotFound, a8 as WikiDuplicateHashError, a9 as WikiEdge, aa as WikiEvent, ab as WikiFact, ac as WikiGraphNodeOwnershipConflict, ad as WikiIngestEmptyError, ae as WikiInvalidReadOptions, af as WikiMemoryTestAccess, ag as WikiOutboxEvent, ah as WikiParseError, ai as WikiSourceRefHashCollision, aj as WikiStrictOntologyViolation, ak as WikiTask, al as WikiTransactionError, am as validateManifest } from './testing-d1SrpDvg.js';
|
|
3
3
|
import { OkfFile } from '@equationalapplications/core-okf';
|
|
4
4
|
import 'minisearch';
|
|
5
5
|
|
package/dist/index.js
CHANGED
|
@@ -1144,15 +1144,16 @@ var EntryRepository = class extends BaseRepository {
|
|
|
1144
1144
|
* fact, not only immutable_document rows, so the source-type restriction has
|
|
1145
1145
|
* to be applied here, after retrieval.
|
|
1146
1146
|
*/
|
|
1147
|
-
async findAnchorRowsByIds(entityId, ids, tx) {
|
|
1147
|
+
async findAnchorRowsByIds(entityId, ids, tx, opts) {
|
|
1148
1148
|
if (ids.length === 0) return [];
|
|
1149
1149
|
const executor = this.getExecutor(tx);
|
|
1150
|
+
const columns = opts?.withBody ? "id, title, source_ref, body, lifecycle_status" : "id, title, source_ref";
|
|
1150
1151
|
const rows = [];
|
|
1151
1152
|
for (let i = 0; i < ids.length; i += this.chunkSize) {
|
|
1152
1153
|
const chunk = ids.slice(i, i + this.chunkSize);
|
|
1153
1154
|
const placeholders = chunk.map(() => "?").join(", ");
|
|
1154
1155
|
const chunkRows = await executor.getAllAsync(
|
|
1155
|
-
`SELECT
|
|
1156
|
+
`SELECT ${columns} FROM ${this.prefix}entries
|
|
1156
1157
|
WHERE entity_id = ? AND deleted_at IS NULL
|
|
1157
1158
|
AND source_type = 'immutable_document'
|
|
1158
1159
|
AND id IN (${placeholders})`,
|
|
@@ -4154,6 +4155,19 @@ function validateTags(tags) {
|
|
|
4154
4155
|
if (!Array.isArray(tags)) return [];
|
|
4155
4156
|
return tags.filter((t) => typeof t === "string").map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0 && t.length <= 40).slice(0, 6);
|
|
4156
4157
|
}
|
|
4158
|
+
var MAX_EVIDENCE_QUOTES = 10;
|
|
4159
|
+
function normalizeEvidence(raw) {
|
|
4160
|
+
if (!Array.isArray(raw)) return void 0;
|
|
4161
|
+
const out = [];
|
|
4162
|
+
for (const entry of raw) {
|
|
4163
|
+
if (typeof entry !== "string") continue;
|
|
4164
|
+
const trimmed = entry.trim();
|
|
4165
|
+
if (!trimmed) continue;
|
|
4166
|
+
out.push(trimmed);
|
|
4167
|
+
if (out.length > MAX_EVIDENCE_QUOTES) break;
|
|
4168
|
+
}
|
|
4169
|
+
return out;
|
|
4170
|
+
}
|
|
4157
4171
|
function validateFact(fact) {
|
|
4158
4172
|
if (typeof fact?.title !== "string" || typeof fact?.body !== "string") return null;
|
|
4159
4173
|
const title = clip(fact.title, 80);
|
|
@@ -4161,13 +4175,17 @@ function validateFact(fact) {
|
|
|
4161
4175
|
if (!title || !body) return null;
|
|
4162
4176
|
let confidence = fact.confidence;
|
|
4163
4177
|
if (confidence !== "certain" && confidence !== "tentative") confidence = "inferred";
|
|
4164
|
-
|
|
4178
|
+
const valid = {
|
|
4165
4179
|
...fact,
|
|
4166
4180
|
title,
|
|
4167
4181
|
body,
|
|
4168
4182
|
confidence,
|
|
4169
4183
|
tags: validateTags(fact.tags)
|
|
4170
4184
|
};
|
|
4185
|
+
const evidence = normalizeEvidence(fact.evidence);
|
|
4186
|
+
if (evidence) valid.evidence = evidence;
|
|
4187
|
+
else delete valid.evidence;
|
|
4188
|
+
return valid;
|
|
4171
4189
|
}
|
|
4172
4190
|
function validateTask(task) {
|
|
4173
4191
|
if (typeof task?.description !== "string") return null;
|
|
@@ -4212,6 +4230,60 @@ function jaccardScore(a, b) {
|
|
|
4212
4230
|
return intersection.size / union.size;
|
|
4213
4231
|
}
|
|
4214
4232
|
|
|
4233
|
+
// src/utils/grounding.ts
|
|
4234
|
+
var GROUNDING_VERIFIER = "process:grounding-check";
|
|
4235
|
+
var WRITERS = ["ingest", "librarian", "heal"];
|
|
4236
|
+
function positiveInt(value, fallback) {
|
|
4237
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : fallback;
|
|
4238
|
+
}
|
|
4239
|
+
function resolveGrounding(config) {
|
|
4240
|
+
if (!config || config.mode !== "draft") return null;
|
|
4241
|
+
const writers = Array.isArray(config.writers) ? config.writers.filter((w) => WRITERS.includes(w)) : ["ingest"];
|
|
4242
|
+
return {
|
|
4243
|
+
writers: new Set(writers),
|
|
4244
|
+
minEvidenceChars: positiveInt(config.minEvidenceChars, 20),
|
|
4245
|
+
// Never ask for more quotes than checkGrounding accepts (MAX_EVIDENCE_QUOTES).
|
|
4246
|
+
maxEvidence: Math.min(MAX_EVIDENCE_QUOTES, positiveInt(config.maxEvidence, 3)),
|
|
4247
|
+
maxEvidenceChars: positiveInt(config.maxEvidenceChars, 300)
|
|
4248
|
+
};
|
|
4249
|
+
}
|
|
4250
|
+
function normalizeForGrounding(text) {
|
|
4251
|
+
return text.normalize("NFKC").replace(/\s+/g, " ").trim();
|
|
4252
|
+
}
|
|
4253
|
+
function buildGroundingCorpus(parts) {
|
|
4254
|
+
return parts.filter((p) => typeof p === "string").map(normalizeForGrounding);
|
|
4255
|
+
}
|
|
4256
|
+
function checkGrounding(evidence, normalizedCorpus, cfg) {
|
|
4257
|
+
const quotes = evidence ?? [];
|
|
4258
|
+
if (quotes.length > MAX_EVIDENCE_QUOTES) return { status: "failed", reason: "too_many_quotes" };
|
|
4259
|
+
if (quotes.length === 0) return { status: "missing", reason: "no_evidence" };
|
|
4260
|
+
const qualifying = quotes.map(normalizeForGrounding).filter((q) => q.length >= cfg.minEvidenceChars);
|
|
4261
|
+
if (qualifying.length === 0) return { status: "missing", reason: "evidence_too_short" };
|
|
4262
|
+
if (qualifying.some((q) => !normalizedCorpus.some((part) => part.includes(q)))) {
|
|
4263
|
+
return { status: "failed", reason: "quote_not_found" };
|
|
4264
|
+
}
|
|
4265
|
+
return {
|
|
4266
|
+
status: "grounded",
|
|
4267
|
+
retained: qualifying.slice(0, cfg.maxEvidence).map((q) => safeSlice(q, 0, cfg.maxEvidenceChars))
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
4270
|
+
function groundingOutcome(verdict, now) {
|
|
4271
|
+
if (verdict.status === "grounded") {
|
|
4272
|
+
return {
|
|
4273
|
+
trust: {
|
|
4274
|
+
lifecycle_status: "stable",
|
|
4275
|
+
okf_verified: [{ by: GROUNDING_VERIFIER, at: new Date(now).toISOString() }],
|
|
4276
|
+
last_verified_at: now,
|
|
4277
|
+
last_verified_by: GROUNDING_VERIFIER
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
}
|
|
4281
|
+
return {
|
|
4282
|
+
trust: { lifecycle_status: "draft" },
|
|
4283
|
+
diagnostic: { code: verdict.status === "missing" ? "grounding_missing" : "grounding_failed", reason: verdict.reason }
|
|
4284
|
+
};
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4215
4287
|
// src/utils/diagnostics.ts
|
|
4216
4288
|
var SEVERITY = {
|
|
4217
4289
|
ingest_chunk_failed: "warn",
|
|
@@ -4341,17 +4413,40 @@ Return ONLY a valid JSON object matching this schema:
|
|
|
4341
4413
|
}
|
|
4342
4414
|
If no manifest type fits a fact, omit that fact from "classifications" entirely \u2014 do not guess.
|
|
4343
4415
|
When echoing an existing fact's title verbatim into "target_title", preserve every JSON escape sequence (\\", \\n, \\\\, \\/) exactly as it appeared in the input body \u2014 do not strip backslashes, do not add unescaped quotes. Do not return markdown, just raw JSON.`;
|
|
4416
|
+
var GROUNDING_SOURCE = {
|
|
4417
|
+
ingest: { key: "facts", source: "the document chunk" },
|
|
4418
|
+
librarian: { key: "facts", source: 'the "summary" text of the events' },
|
|
4419
|
+
heal: { key: "newFacts", source: 'the "summary" text of the recent events or the "body" text of the document anchors' }
|
|
4420
|
+
};
|
|
4421
|
+
function groundingEvidenceBlock(writer, cfg) {
|
|
4422
|
+
const { key, source } = GROUNDING_SOURCE[writer];
|
|
4423
|
+
return `EVIDENCE REQUIREMENT: every object in "${key}" must also carry an "evidence" array of 1 to ${cfg.maxEvidence} quotes. Each quote must be an exact substring copied character-for-character from ${source} (the SOURCE section), at least ${cfg.minEvidenceChars} characters long. Do not paraphrase. Do not quote these instructions, the ontology manifest, or any existing fact. A fact whose quotes cannot be found in the SOURCE section is stored as an unreviewed draft.
|
|
4424
|
+
"evidence": ["exact substring copied from the SOURCE section"]`;
|
|
4425
|
+
}
|
|
4344
4426
|
|
|
4345
4427
|
// src/utils/healConstants.ts
|
|
4346
4428
|
var HEAL_MAX_ANCHORS = 50;
|
|
4347
4429
|
var HEAL_ANCHORS_PER_CANDIDATE = 4;
|
|
4348
4430
|
var HEAL_MAX_FACT_BODY_CHARS_L3 = 4e3;
|
|
4349
4431
|
var HEAL_MAX_TASKS = 50;
|
|
4432
|
+
var HEAL_ANCHOR_BODY_CHARS = 800;
|
|
4350
4433
|
|
|
4351
4434
|
// src/services/PromptService.ts
|
|
4352
4435
|
var PromptService = class {
|
|
4353
|
-
constructor(globalOverrides) {
|
|
4436
|
+
constructor(globalOverrides, grounding = null) {
|
|
4354
4437
|
this.globalOverrides = globalOverrides;
|
|
4438
|
+
this.grounding = grounding;
|
|
4439
|
+
}
|
|
4440
|
+
/** The resolved grounding config when `writer` is in `grounding.writers`; otherwise null (spec §6.2). */
|
|
4441
|
+
groundingFor(writer) {
|
|
4442
|
+
return this.grounding?.writers.has(writer) ? this.grounding : null;
|
|
4443
|
+
}
|
|
4444
|
+
/** Appended after any override and after ontology context, so it is always last. */
|
|
4445
|
+
appendGrounding(systemPrompt, writer) {
|
|
4446
|
+
const cfg = this.groundingFor(writer);
|
|
4447
|
+
return cfg ? `${systemPrompt}
|
|
4448
|
+
|
|
4449
|
+
${groundingEvidenceBlock(writer, cfg)}` : systemPrompt;
|
|
4355
4450
|
}
|
|
4356
4451
|
hydrate(template, variables) {
|
|
4357
4452
|
return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (_match, key) => {
|
|
@@ -4381,13 +4476,13 @@ ${ctx.ontologyModeInstructions}`;
|
|
|
4381
4476
|
const hasDocumentChunk = /\{\{\s*documentChunk\s*\}\}/.test(template);
|
|
4382
4477
|
if (hasDocumentChunk || this.hasOntologyPlaceholders(template)) {
|
|
4383
4478
|
return {
|
|
4384
|
-
systemPrompt: this.buildSystemPrompt(template, { documentChunk }, ontologyContext),
|
|
4479
|
+
systemPrompt: this.appendGrounding(this.buildSystemPrompt(template, { documentChunk }, ontologyContext), "ingest"),
|
|
4385
4480
|
userPrompt: hasDocumentChunk ? "Please extract the facts." : `Document Chunk:
|
|
4386
4481
|
${documentChunk}`
|
|
4387
4482
|
};
|
|
4388
4483
|
}
|
|
4389
4484
|
return {
|
|
4390
|
-
systemPrompt: this.appendOntology(template, ontologyContext),
|
|
4485
|
+
systemPrompt: this.appendGrounding(this.appendOntology(template, ontologyContext), "ingest"),
|
|
4391
4486
|
userPrompt: `Document Chunk:
|
|
4392
4487
|
${documentChunk}`
|
|
4393
4488
|
};
|
|
@@ -4396,23 +4491,27 @@ ${documentChunk}`
|
|
|
4396
4491
|
const template = runtimeOverride ?? this.globalOverrides?.librarianSystemPrompt ?? LIBRARIAN_SYSTEM_PROMPT;
|
|
4397
4492
|
const hasEvents = /\{\{\s*events\s*\}\}/.test(template);
|
|
4398
4493
|
const hasCurrentFacts = /\{\{\s*currentFacts\s*\}\}/.test(template);
|
|
4494
|
+
const eventsShown = hasEvents || !hasCurrentFacts;
|
|
4495
|
+
const corpusField = this.groundingFor("librarian") ? { groundingCorpus: buildGroundingCorpus(eventsShown ? events.map(summaryOf) : []) } : {};
|
|
4399
4496
|
if (hasEvents || hasCurrentFacts || this.hasOntologyPlaceholders(template)) {
|
|
4400
4497
|
return {
|
|
4401
|
-
systemPrompt: this.buildSystemPrompt(template, { events, currentFacts }, ontologyContext),
|
|
4498
|
+
systemPrompt: this.appendGrounding(this.buildSystemPrompt(template, { events, currentFacts }, ontologyContext), "librarian"),
|
|
4402
4499
|
userPrompt: hasEvents || hasCurrentFacts ? "Please synthesize the context." : `Events:
|
|
4403
4500
|
${JSON.stringify(events, null, 2)}
|
|
4404
4501
|
|
|
4405
4502
|
Current Facts:
|
|
4406
|
-
${JSON.stringify(currentFacts, null, 2)}
|
|
4503
|
+
${JSON.stringify(currentFacts, null, 2)}`,
|
|
4504
|
+
...corpusField
|
|
4407
4505
|
};
|
|
4408
4506
|
}
|
|
4409
4507
|
return {
|
|
4410
|
-
systemPrompt: this.appendOntology(template, ontologyContext),
|
|
4508
|
+
systemPrompt: this.appendGrounding(this.appendOntology(template, ontologyContext), "librarian"),
|
|
4411
4509
|
userPrompt: `Events:
|
|
4412
4510
|
${JSON.stringify(events, null, 2)}
|
|
4413
4511
|
|
|
4414
4512
|
Current Facts:
|
|
4415
|
-
${JSON.stringify(currentFacts, null, 2)}
|
|
4513
|
+
${JSON.stringify(currentFacts, null, 2)}`,
|
|
4514
|
+
...corpusField
|
|
4416
4515
|
};
|
|
4417
4516
|
}
|
|
4418
4517
|
/**
|
|
@@ -4442,40 +4541,54 @@ ${JSON.stringify(currentFacts, null, 2)}`
|
|
|
4442
4541
|
const effectiveEvents = attemptLevel >= 2 ? [] : recentEvents;
|
|
4443
4542
|
const maxAnchors = Math.max(1, Math.min(HEAL_MAX_ANCHORS, healCandidates.length * HEAL_ANCHORS_PER_CANDIDATE));
|
|
4444
4543
|
const effectiveAnchors = documentAnchors.slice(0, maxAnchors);
|
|
4544
|
+
const template = runtimeOverride ?? this.globalOverrides?.healSystemPrompt ?? HEAL_SYSTEM_PROMPT;
|
|
4545
|
+
const hasRecentEvents = /\{\{\s*recentEvents\s*\}\}/.test(template);
|
|
4546
|
+
const hasDocumentAnchors = /\{\{\s*documentAnchors\s*\}\}/.test(template);
|
|
4547
|
+
const usesPlaceholders = /\{\{\s*healCandidates\s*\}\}/.test(template) || hasDocumentAnchors || /\{\{\s*allTasks\s*\}\}/.test(template) || hasRecentEvents;
|
|
4548
|
+
const healGrounding = this.groundingFor("heal");
|
|
4549
|
+
const promptAnchors = healGrounding ? effectiveAnchors.map(toGroundingAnchor) : effectiveAnchors;
|
|
4550
|
+
const eventsShown = !usesPlaceholders || hasRecentEvents;
|
|
4551
|
+
const anchorsShown = !usesPlaceholders || hasDocumentAnchors;
|
|
4552
|
+
const groundingCorpus = healGrounding ? buildGroundingCorpus([
|
|
4553
|
+
...eventsShown ? effectiveEvents.map(summaryOf) : [],
|
|
4554
|
+
...anchorsShown ? effectiveAnchors.map((a, i) => ({ status: a?.lifecycle_status, shown: promptAnchors[i] })).filter((x) => x.status !== "draft").map((x) => x.shown?.body) : []
|
|
4555
|
+
]) : null;
|
|
4445
4556
|
const { shapedCandidates, degraded } = applyBodyTruncation(
|
|
4446
4557
|
healCandidates,
|
|
4447
4558
|
attemptLevel,
|
|
4448
4559
|
bodyTruncationChars
|
|
4449
4560
|
);
|
|
4450
|
-
const
|
|
4451
|
-
if (
|
|
4561
|
+
const corpusField = groundingCorpus !== null ? { groundingCorpus } : {};
|
|
4562
|
+
if (usesPlaceholders) {
|
|
4452
4563
|
return {
|
|
4453
4564
|
prompts: {
|
|
4454
|
-
systemPrompt: this.hydrate(template, {
|
|
4565
|
+
systemPrompt: this.appendGrounding(this.hydrate(template, {
|
|
4455
4566
|
healCandidates: shapedCandidates,
|
|
4456
|
-
documentAnchors:
|
|
4567
|
+
documentAnchors: promptAnchors,
|
|
4457
4568
|
allTasks: effectiveTasks,
|
|
4458
4569
|
recentEvents: effectiveEvents
|
|
4459
|
-
}),
|
|
4570
|
+
}), "heal"),
|
|
4460
4571
|
userPrompt: "Please heal the memory graph."
|
|
4461
4572
|
},
|
|
4462
|
-
degraded
|
|
4573
|
+
degraded,
|
|
4574
|
+
...corpusField
|
|
4463
4575
|
};
|
|
4464
4576
|
}
|
|
4465
4577
|
return {
|
|
4466
4578
|
prompts: {
|
|
4467
|
-
systemPrompt: template,
|
|
4579
|
+
systemPrompt: this.appendGrounding(template, "heal"),
|
|
4468
4580
|
userPrompt: `Heal Candidates:
|
|
4469
4581
|
${JSON.stringify(shapedCandidates, null, 2)}
|
|
4470
4582
|
Document Anchors (DO NOT MODIFY OR DELETE):
|
|
4471
|
-
${JSON.stringify(
|
|
4583
|
+
${JSON.stringify(promptAnchors, null, 2)}
|
|
4472
4584
|
All Tasks:
|
|
4473
4585
|
${JSON.stringify(effectiveTasks, null, 2)}
|
|
4474
4586
|
Recent Events:
|
|
4475
4587
|
${JSON.stringify(effectiveEvents, null, 2)}
|
|
4476
4588
|
The following document anchors are provided for contradiction detection only. Do not include them in \`downgraded\`, \`deleted\`, or \`newFacts\`.`
|
|
4477
4589
|
},
|
|
4478
|
-
degraded
|
|
4590
|
+
degraded,
|
|
4591
|
+
...corpusField
|
|
4479
4592
|
};
|
|
4480
4593
|
}
|
|
4481
4594
|
buildOntologyBackfillPrompt(facts, runtimeOverride, ontologyContext) {
|
|
@@ -4525,6 +4638,19 @@ function applyBodyTruncation(candidates, attemptLevel, bodyTruncationChars) {
|
|
|
4525
4638
|
}
|
|
4526
4639
|
return { shapedCandidates, degraded };
|
|
4527
4640
|
}
|
|
4641
|
+
function summaryOf(event) {
|
|
4642
|
+
return event?.summary;
|
|
4643
|
+
}
|
|
4644
|
+
function toGroundingAnchor(anchor) {
|
|
4645
|
+
if (typeof anchor !== "object" || anchor === null) return anchor;
|
|
4646
|
+
const a = anchor;
|
|
4647
|
+
return {
|
|
4648
|
+
id: a.id,
|
|
4649
|
+
title: a.title,
|
|
4650
|
+
source_ref: a.source_ref,
|
|
4651
|
+
body: typeof a.body === "string" ? safeSlice(a.body, 0, HEAL_ANCHOR_BODY_CHARS) : ""
|
|
4652
|
+
};
|
|
4653
|
+
}
|
|
4528
4654
|
|
|
4529
4655
|
// src/utils/chunkingDefaults.ts
|
|
4530
4656
|
var DEFAULT_MAX_CHUNK_LENGTH = 12e3;
|
|
@@ -4554,7 +4680,7 @@ var IngestionService = class {
|
|
|
4554
4680
|
this.jobManager = jobManager;
|
|
4555
4681
|
this.embeddingService = embeddingService;
|
|
4556
4682
|
this.ontologyService = ontologyService;
|
|
4557
|
-
this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
|
|
4683
|
+
this.promptService = promptService ?? new PromptService(this.options.config?.prompts, resolveGrounding(this.options.config?.grounding));
|
|
4558
4684
|
}
|
|
4559
4685
|
async ingestDocument(entityId, params, opts) {
|
|
4560
4686
|
const sourceRef = normalizeSourceRef(params.sourceRef);
|
|
@@ -4587,6 +4713,7 @@ var IngestionService = class {
|
|
|
4587
4713
|
const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
|
|
4588
4714
|
if (chunks.length === 0) return zeroChunkResult();
|
|
4589
4715
|
const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
|
|
4716
|
+
const ingestGrounding = this.promptService.groundingFor("ingest");
|
|
4590
4717
|
const chunkResults = await withConcurrency(
|
|
4591
4718
|
chunks.map((chunk, chunkIndex) => async () => {
|
|
4592
4719
|
const { systemPrompt, userPrompt } = this.promptService.buildIngestPrompt(
|
|
@@ -4610,11 +4737,16 @@ var IngestionService = class {
|
|
|
4610
4737
|
rejected.push({ itemIndex, reason: factRejectionReason(raw) });
|
|
4611
4738
|
}
|
|
4612
4739
|
});
|
|
4740
|
+
const verdicts = ingestGrounding ? (() => {
|
|
4741
|
+
const corpus = buildGroundingCorpus([chunk]);
|
|
4742
|
+
return facts.map((f) => checkGrounding(f.evidence, corpus, ingestGrounding));
|
|
4743
|
+
})() : [];
|
|
4613
4744
|
return {
|
|
4614
4745
|
status: "ok",
|
|
4615
4746
|
facts,
|
|
4616
4747
|
itemIndexes,
|
|
4617
4748
|
rejected,
|
|
4749
|
+
verdicts,
|
|
4618
4750
|
ontology_updates: result2.ontology_updates
|
|
4619
4751
|
};
|
|
4620
4752
|
} catch (e) {
|
|
@@ -4659,6 +4791,7 @@ var IngestionService = class {
|
|
|
4659
4791
|
const failures = [];
|
|
4660
4792
|
const seen = /* @__PURE__ */ new Set();
|
|
4661
4793
|
const orderedChunkFacts = [];
|
|
4794
|
+
const groundingLedger = /* @__PURE__ */ new Map();
|
|
4662
4795
|
const diagBuffer = new DiagnosticBuffer();
|
|
4663
4796
|
const diagBase = { entityId, operation: "ingest", trigger: "call" };
|
|
4664
4797
|
for (const [chunkIndex, slot] of chunkResults.entries()) {
|
|
@@ -4677,6 +4810,7 @@ var IngestionService = class {
|
|
|
4677
4810
|
if (!seen.has(normalizedTitle)) {
|
|
4678
4811
|
seen.add(normalizedTitle);
|
|
4679
4812
|
dedupedFacts.push(fact);
|
|
4813
|
+
if (ingestGrounding) groundingLedger.set(fact, { verdict: slot.verdicts[k], chunkIndex, itemIndex: slot.itemIndexes[k] });
|
|
4680
4814
|
} else {
|
|
4681
4815
|
diagBuffer.push({
|
|
4682
4816
|
...diagBase,
|
|
@@ -4713,7 +4847,7 @@ var IngestionService = class {
|
|
|
4713
4847
|
try {
|
|
4714
4848
|
if (failedChunks === 0) {
|
|
4715
4849
|
const fullResult = await this.db.withTransactionAsync(async (tx) => {
|
|
4716
|
-
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer);
|
|
4850
|
+
return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, groundingLedger);
|
|
4717
4851
|
});
|
|
4718
4852
|
deletedSourceFactIds.push(...fullResult.deletedSourceFactIds);
|
|
4719
4853
|
insertedFacts.push(...fullResult.insertedFacts);
|
|
@@ -4721,7 +4855,7 @@ var IngestionService = class {
|
|
|
4721
4855
|
const partialResult = await this.db.withTransactionAsync(async (tx) => {
|
|
4722
4856
|
const flat = [];
|
|
4723
4857
|
for (const slot of orderedChunkFacts) flat.push(...slot.facts);
|
|
4724
|
-
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer);
|
|
4858
|
+
return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer, groundingLedger);
|
|
4725
4859
|
});
|
|
4726
4860
|
insertedFacts.push(...partialResult.insertedDescriptors);
|
|
4727
4861
|
}
|
|
@@ -4863,7 +4997,8 @@ var IngestionService = class {
|
|
|
4863
4997
|
last_accessed_at: null,
|
|
4864
4998
|
access_count: 0,
|
|
4865
4999
|
deleted_at: null,
|
|
4866
|
-
okf_type: normalized.okf_type
|
|
5000
|
+
okf_type: normalized.okf_type,
|
|
5001
|
+
...opts?.nodeTrust?.get(node.id)
|
|
4867
5002
|
};
|
|
4868
5003
|
wikiFacts.push(wikiFact);
|
|
4869
5004
|
}
|
|
@@ -4954,9 +5089,10 @@ var IngestionService = class {
|
|
|
4954
5089
|
* On the partial path the caller never invokes this method; the empty
|
|
4955
5090
|
* arrays stay empty.
|
|
4956
5091
|
*/
|
|
4957
|
-
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer) {
|
|
5092
|
+
async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, groundingLedger = /* @__PURE__ */ new Map()) {
|
|
4958
5093
|
const deletedSourceFactIds = [];
|
|
4959
5094
|
const insertedFacts = [];
|
|
5095
|
+
const nodeTrust = /* @__PURE__ */ new Map();
|
|
4960
5096
|
deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
|
|
4961
5097
|
const titleIndex = /* @__PURE__ */ new Map();
|
|
4962
5098
|
const existingFacts = await this.entryRepo.findRecentByEntityId(entityId, 500, tx, sourceRef);
|
|
@@ -5000,6 +5136,20 @@ var IngestionService = class {
|
|
|
5000
5136
|
tags: fact.tags,
|
|
5001
5137
|
confidence: fact.confidence
|
|
5002
5138
|
});
|
|
5139
|
+
const grounding = groundingLedger.get(fact);
|
|
5140
|
+
if (grounding) {
|
|
5141
|
+
const outcome = groundingOutcome(grounding.verdict, now);
|
|
5142
|
+
nodeTrust.set(id, outcome.trust);
|
|
5143
|
+
if (outcome.diagnostic) {
|
|
5144
|
+
diagBuffer.push({
|
|
5145
|
+
entityId,
|
|
5146
|
+
operation: "ingest",
|
|
5147
|
+
trigger: "call",
|
|
5148
|
+
code: outcome.diagnostic.code,
|
|
5149
|
+
detail: { factId: id, sourceRef, chunkIndex: grounding.chunkIndex, itemIndex: grounding.itemIndex, reason: outcome.diagnostic.reason }
|
|
5150
|
+
});
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5003
5153
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
5004
5154
|
titleIndex.set(normalizeTitleKey(fact.title), { id, okf_type: normalized.okf_type });
|
|
5005
5155
|
if (normalized.edges.length > 0) {
|
|
@@ -5031,7 +5181,7 @@ var IngestionService = class {
|
|
|
5031
5181
|
entityId,
|
|
5032
5182
|
{ sourceRef, sourceHash, nodes: hostNodes, edges: hostEdges },
|
|
5033
5183
|
tx,
|
|
5034
|
-
{ strict: false, diag: { buffer: diagBuffer, operation: "ingest" } }
|
|
5184
|
+
{ strict: false, diag: { buffer: diagBuffer, operation: "ingest" }, ...nodeTrust.size > 0 ? { nodeTrust } : {} }
|
|
5035
5185
|
);
|
|
5036
5186
|
return { deletedSourceFactIds, insertedFacts };
|
|
5037
5187
|
}
|
|
@@ -5055,7 +5205,7 @@ var IngestionService = class {
|
|
|
5055
5205
|
* Runs INSIDE the caller's `tx`. Does not open a nested transaction.
|
|
5056
5206
|
* Returns `{ inserted, skippedDuplicate }` for observability.
|
|
5057
5207
|
*/
|
|
5058
|
-
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer) {
|
|
5208
|
+
async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer, groundingLedger = /* @__PURE__ */ new Map()) {
|
|
5059
5209
|
const liveIds = await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false);
|
|
5060
5210
|
const liveFacts = liveIds.length === 0 ? [] : await this.entryRepo.findByIds(liveIds, void 0, tx);
|
|
5061
5211
|
const liveTitles = new Set(liveFacts.map((f) => normalizeTitleKey(f.title)));
|
|
@@ -5078,6 +5228,17 @@ var IngestionService = class {
|
|
|
5078
5228
|
}
|
|
5079
5229
|
liveTitles.add(normalizedTitle);
|
|
5080
5230
|
const id = generateId("fact_");
|
|
5231
|
+
const grounding = groundingLedger.get(fact);
|
|
5232
|
+
const outcome = grounding ? groundingOutcome(grounding.verdict, now) : null;
|
|
5233
|
+
if (grounding && outcome?.diagnostic) {
|
|
5234
|
+
diagBuffer.push({
|
|
5235
|
+
entityId,
|
|
5236
|
+
operation: "ingest",
|
|
5237
|
+
trigger: "call",
|
|
5238
|
+
code: outcome.diagnostic.code,
|
|
5239
|
+
detail: { factId: id, sourceRef, chunkIndex: grounding.chunkIndex, itemIndex: grounding.itemIndex, reason: outcome.diagnostic.reason }
|
|
5240
|
+
});
|
|
5241
|
+
}
|
|
5081
5242
|
const wikiFact = {
|
|
5082
5243
|
id,
|
|
5083
5244
|
entity_id: entityId,
|
|
@@ -5096,7 +5257,8 @@ var IngestionService = class {
|
|
|
5096
5257
|
last_accessed_at: null,
|
|
5097
5258
|
access_count: 0,
|
|
5098
5259
|
deleted_at: null,
|
|
5099
|
-
okf_type: null
|
|
5260
|
+
okf_type: null,
|
|
5261
|
+
...outcome?.trust
|
|
5100
5262
|
};
|
|
5101
5263
|
await this.entryRepo.upsert(wikiFact, tx);
|
|
5102
5264
|
insertedDescriptors.push({
|
|
@@ -5264,7 +5426,7 @@ async function runBatched(args) {
|
|
|
5264
5426
|
}
|
|
5265
5427
|
let result;
|
|
5266
5428
|
try {
|
|
5267
|
-
result = parse(responseText, batch);
|
|
5429
|
+
result = parse(responseText, batch, prompts);
|
|
5268
5430
|
} catch (err) {
|
|
5269
5431
|
await onFailure(batch, err, attemptLevel, false);
|
|
5270
5432
|
return;
|
|
@@ -5345,7 +5507,7 @@ var MaintenanceService = class {
|
|
|
5345
5507
|
this.jobManager = jobManager;
|
|
5346
5508
|
this.embeddingService = embeddingService;
|
|
5347
5509
|
this.ontologyService = ontologyService;
|
|
5348
|
-
this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
|
|
5510
|
+
this.promptService = promptService ?? new PromptService(this.options.config?.prompts, resolveGrounding(this.options.config?.grounding));
|
|
5349
5511
|
}
|
|
5350
5512
|
async runPrune(entityId, options) {
|
|
5351
5513
|
this.jobManager.acquireLock("prune", entityId);
|
|
@@ -5650,8 +5812,10 @@ var MaintenanceService = class {
|
|
|
5650
5812
|
};
|
|
5651
5813
|
});
|
|
5652
5814
|
const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
|
|
5653
|
-
const
|
|
5654
|
-
|
|
5815
|
+
const promptEvents = events.reverse();
|
|
5816
|
+
const librarianGrounding = this.promptService.groundingFor("librarian");
|
|
5817
|
+
const { systemPrompt, userPrompt, groundingCorpus: librarianCorpus = [] } = this.promptService.buildLibrarianPrompt(
|
|
5818
|
+
promptEvents,
|
|
5655
5819
|
currentFacts,
|
|
5656
5820
|
promptOverride,
|
|
5657
5821
|
ontologyContext
|
|
@@ -5720,6 +5884,10 @@ var MaintenanceService = class {
|
|
|
5720
5884
|
const validationDrops = [];
|
|
5721
5885
|
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
|
|
5722
5886
|
for (const drop of validationDrops) diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: id }));
|
|
5887
|
+
const grounding = librarianGrounding ? groundingOutcome(checkGrounding(fact.evidence, librarianCorpus, librarianGrounding), now) : null;
|
|
5888
|
+
if (grounding?.diagnostic) {
|
|
5889
|
+
diagBuffer.push({ ...diagBase, code: grounding.diagnostic.code, detail: { factId: id, itemIndex: validFactItemIndexes[k], reason: grounding.diagnostic.reason } });
|
|
5890
|
+
}
|
|
5723
5891
|
const factObj = {
|
|
5724
5892
|
id,
|
|
5725
5893
|
entity_id: entityId,
|
|
@@ -5735,7 +5903,8 @@ var MaintenanceService = class {
|
|
|
5735
5903
|
last_accessed_at: null,
|
|
5736
5904
|
access_count: 0,
|
|
5737
5905
|
deleted_at: null,
|
|
5738
|
-
okf_type: normalized.okf_type
|
|
5906
|
+
okf_type: normalized.okf_type,
|
|
5907
|
+
...grounding?.trust
|
|
5739
5908
|
};
|
|
5740
5909
|
await this.entryRepo.upsert(factObj, tx);
|
|
5741
5910
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
@@ -5851,11 +6020,14 @@ var MaintenanceService = class {
|
|
|
5851
6020
|
}
|
|
5852
6021
|
const allTasks = await this.taskRepo.findAllPending([entityId], HEAL_MAX_TASKS);
|
|
5853
6022
|
const recentEvents = await this.eventRepo.getRecent(entityId, 20);
|
|
6023
|
+
const healGrounding = this.promptService.groundingFor("heal");
|
|
5854
6024
|
const toPromptShape = (f) => {
|
|
5855
6025
|
const { embedding: _embedding, embedding_blob: _blob, ...rest } = f;
|
|
5856
|
-
|
|
6026
|
+
const shown = healGrounding ? (({ lifecycle_status: _l, okf_verified: _v, last_verified_at: _a, last_verified_by: _b, ...r }) => r)(rest) : rest;
|
|
6027
|
+
return { ...shown, tags: typeof shown.tags === "string" ? JSON.parse(shown.tags) : shown.tags };
|
|
5857
6028
|
};
|
|
5858
6029
|
const anchorCache = /* @__PURE__ */ new Map();
|
|
6030
|
+
const corpusByPrompt = /* @__PURE__ */ new WeakMap();
|
|
5859
6031
|
const degraded = [];
|
|
5860
6032
|
const outcome = await runBatched({
|
|
5861
6033
|
items: healCandidates,
|
|
@@ -5868,9 +6040,10 @@ var MaintenanceService = class {
|
|
|
5868
6040
|
// not overfetch the same 200 keyword hits a 25-fact batch once did.
|
|
5869
6041
|
// buildHealPrompt applies the matching cap on its side — values must match.
|
|
5870
6042
|
Math.min(HEAL_MAX_ANCHORS, batch.length * HEAL_ANCHORS_PER_CANDIDATE),
|
|
5871
|
-
anchorCache
|
|
6043
|
+
anchorCache,
|
|
6044
|
+
healGrounding !== null
|
|
5872
6045
|
);
|
|
5873
|
-
const { prompts, degraded: batchDegraded } = await this.promptService.buildHealPrompt(
|
|
6046
|
+
const { prompts, degraded: batchDegraded, groundingCorpus } = await this.promptService.buildHealPrompt(
|
|
5874
6047
|
batch.map(toPromptShape),
|
|
5875
6048
|
documentAnchors,
|
|
5876
6049
|
allTasks,
|
|
@@ -5880,16 +6053,18 @@ var MaintenanceService = class {
|
|
|
5880
6053
|
bodyTruncationChars
|
|
5881
6054
|
);
|
|
5882
6055
|
degraded.push(...batchDegraded);
|
|
6056
|
+
if (groundingCorpus !== void 0) corpusByPrompt.set(prompts, groundingCorpus);
|
|
5883
6057
|
return prompts;
|
|
5884
6058
|
},
|
|
5885
6059
|
call: (prompts) => this.options.llmProvider.generateText(prompts),
|
|
5886
|
-
parse: (responseText, batch) => {
|
|
6060
|
+
parse: (responseText, batch, prompts) => {
|
|
5887
6061
|
const result = parseJsonResponse(responseText);
|
|
5888
6062
|
return {
|
|
5889
6063
|
batch,
|
|
5890
6064
|
downgraded: Array.isArray(result.downgraded) ? result.downgraded : [],
|
|
5891
6065
|
deleted: Array.isArray(result.deleted) ? result.deleted : [],
|
|
5892
|
-
newFacts: Array.isArray(result.newFacts) ? result.newFacts : []
|
|
6066
|
+
newFacts: Array.isArray(result.newFacts) ? result.newFacts : [],
|
|
6067
|
+
corpus: corpusByPrompt.get(prompts) ?? null
|
|
5893
6068
|
};
|
|
5894
6069
|
},
|
|
5895
6070
|
maxOutputTokens: this.options.llmProvider.maxOutputTokens,
|
|
@@ -5904,6 +6079,7 @@ var MaintenanceService = class {
|
|
|
5904
6079
|
const safeDeletedSet = /* @__PURE__ */ new Set();
|
|
5905
6080
|
const newFacts = [];
|
|
5906
6081
|
const newFactItemIndexes = [];
|
|
6082
|
+
const newFactCorpora = [];
|
|
5907
6083
|
for (const batchResult of outcome.results) {
|
|
5908
6084
|
const mutableIds = new Set(batchResult.batch.map((f) => f.id));
|
|
5909
6085
|
for (const id of batchResult.downgraded) if (mutableIds.has(id)) safeDowngradedSet.add(id);
|
|
@@ -5911,6 +6087,7 @@ var MaintenanceService = class {
|
|
|
5911
6087
|
batchResult.newFacts.forEach((raw, itemIndex) => {
|
|
5912
6088
|
newFacts.push(raw);
|
|
5913
6089
|
newFactItemIndexes.push(itemIndex);
|
|
6090
|
+
newFactCorpora.push(batchResult.corpus);
|
|
5914
6091
|
});
|
|
5915
6092
|
}
|
|
5916
6093
|
const safeDowngraded = Array.from(safeDowngradedSet);
|
|
@@ -5918,12 +6095,14 @@ var MaintenanceService = class {
|
|
|
5918
6095
|
const diagBuffer = new DiagnosticBuffer();
|
|
5919
6096
|
const validNewFacts = [];
|
|
5920
6097
|
const validNewFactItemIndexes = [];
|
|
6098
|
+
const validNewFactCorpora = [];
|
|
5921
6099
|
newFacts.forEach((raw, k) => {
|
|
5922
6100
|
const itemIndex = newFactItemIndexes[k];
|
|
5923
6101
|
const valid = validateFact(raw);
|
|
5924
6102
|
if (valid) {
|
|
5925
6103
|
validNewFacts.push(valid);
|
|
5926
6104
|
validNewFactItemIndexes.push(itemIndex);
|
|
6105
|
+
validNewFactCorpora.push(newFactCorpora[k]);
|
|
5927
6106
|
} else {
|
|
5928
6107
|
diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
|
|
5929
6108
|
}
|
|
@@ -5953,6 +6132,10 @@ var MaintenanceService = class {
|
|
|
5953
6132
|
continue;
|
|
5954
6133
|
}
|
|
5955
6134
|
const id = generateId("fact_");
|
|
6135
|
+
const grounding = healGrounding ? groundingOutcome(checkGrounding(fact.evidence, validNewFactCorpora[k] ?? [], healGrounding), now) : null;
|
|
6136
|
+
if (grounding?.diagnostic) {
|
|
6137
|
+
diagBuffer.push({ ...diagBase, code: grounding.diagnostic.code, detail: { factId: id, itemIndex: validNewFactItemIndexes[k], reason: grounding.diagnostic.reason } });
|
|
6138
|
+
}
|
|
5956
6139
|
const factObj = {
|
|
5957
6140
|
id,
|
|
5958
6141
|
entity_id: entityId,
|
|
@@ -5967,7 +6150,8 @@ var MaintenanceService = class {
|
|
|
5967
6150
|
updated_at: now,
|
|
5968
6151
|
last_accessed_at: null,
|
|
5969
6152
|
access_count: 0,
|
|
5970
|
-
deleted_at: null
|
|
6153
|
+
deleted_at: null,
|
|
6154
|
+
...grounding?.trust
|
|
5971
6155
|
};
|
|
5972
6156
|
await this.entryRepo.upsert(factObj, tx);
|
|
5973
6157
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
@@ -6331,7 +6515,7 @@ var MaintenanceService = class {
|
|
|
6331
6515
|
* batches that reduce to the same query share one lookup. Caller-owned and
|
|
6332
6516
|
* per-pass — see the call site in doRunHeal.
|
|
6333
6517
|
*/
|
|
6334
|
-
async _selectHealAnchors(entityId, batch, cap = HEAL_MAX_ANCHORS, cache) {
|
|
6518
|
+
async _selectHealAnchors(entityId, batch, cap = HEAL_MAX_ANCHORS, cache, withBodies = false) {
|
|
6335
6519
|
const query = batch.map((f) => f.title).join(" ").trim();
|
|
6336
6520
|
if (!query) return [];
|
|
6337
6521
|
const cached = cache?.get(query);
|
|
@@ -6344,7 +6528,7 @@ var MaintenanceService = class {
|
|
|
6344
6528
|
const hitIds = hits.map((h) => h.id);
|
|
6345
6529
|
const anchors = [];
|
|
6346
6530
|
if (hitIds.length > 0) {
|
|
6347
|
-
const rows = await this.entryRepo.findAnchorRowsByIds(entityId, hitIds);
|
|
6531
|
+
const rows = withBodies ? await this.entryRepo.findAnchorRowsByIds(entityId, hitIds, void 0, { withBody: true }) : await this.entryRepo.findAnchorRowsByIds(entityId, hitIds);
|
|
6348
6532
|
const byId = new Map(rows.map((r) => [r.id, r]));
|
|
6349
6533
|
for (const id of hitIds) {
|
|
6350
6534
|
const row = byId.get(id);
|
|
@@ -8279,7 +8463,7 @@ var WikiMemory = class {
|
|
|
8279
8463
|
this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
|
|
8280
8464
|
this.searchService = new SearchService(this.entryRepo);
|
|
8281
8465
|
this.jobManager = new JobManager(this.prefix);
|
|
8282
|
-
this.promptService = new PromptService(options.config?.prompts);
|
|
8466
|
+
this.promptService = new PromptService(options.config?.prompts, resolveGrounding(options.config?.grounding));
|
|
8283
8467
|
this.ingestionService = new IngestionService(
|
|
8284
8468
|
this.db,
|
|
8285
8469
|
this.prefix,
|