@equationalapplications/core-llm-wiki 7.8.0 → 7.9.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 +9 -0
- package/dist/{chunk-M76CF2Q6.mjs → chunk-7FRLQ7ZO.mjs} +83 -129
- package/dist/chunk-7FRLQ7ZO.mjs.map +1 -0
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +305 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +227 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-CWZaXKLj.d.mts → testing-Bf6etdUy.d.mts} +156 -26
- package/dist/{testing-CWZaXKLj.d.ts → testing-Bf6etdUy.d.ts} +156 -26
- package/dist/testing.d.mts +1 -2
- package/dist/testing.d.ts +1 -2
- package/dist/testing.js +83 -131
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-M76CF2Q6.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { BaseRepository, WikiGraphNodeOwnershipConflict, liveAtSql, __privateAdd, MetadataRepository, EmbeddingService, SearchService, TemporalService, JobManager, PromptService, resolveGrounding, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, DiagnosticBuffer, validateManifest, WikiDraftNotFound, generateId, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, edgeTripleAllowed, MAX_EMBED_ATTEMPTS, UsageMeter, WikiBusyError, WikiInvalidReadOptions, isLiveAt, packFactsByBudget, factTokenCost, WikiTransactionError, extractSqliteCode } from './chunk-
|
|
2
|
-
export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiSupersedeError, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-
|
|
1
|
+
import { BaseRepository, WikiGraphNodeOwnershipConflict, liveAtSql, __privateAdd, MetadataRepository, EmbeddingService, SearchService, TemporalService, JobManager, PromptService, resolveGrounding, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, DiagnosticBuffer, validateManifest, WikiDraftNotFound, generateId, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, edgeTripleAllowed, MAX_EMBED_ATTEMPTS, UsageMeter, WikiBusyError, WikiInvalidReadOptions, isLiveAt, packFactsByBudget, factTokenCost, WikiTransactionError, extractSqliteCode } from './chunk-7FRLQ7ZO.mjs';
|
|
2
|
+
export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiSupersedeError, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-7FRLQ7ZO.mjs';
|
|
3
3
|
import { deriveTrustTier, isStaleAfter, appendRelatedSection, buildConceptDocument, buildLogMd, buildEntityIndexMd, buildRootIndexMd, isAllowedOkfPath, parseRootIndexMd, parseEntityIndexMd, parseConcept, splitRelatedSection, extractMarkdownLinks, parseLogMd, parseEventIdComment, latestVerified, formatSourcesJson, appendEventIdComment, parseVerifiedFlexible, parseCitationsList } from '@equationalapplications/core-okf';
|
|
4
|
+
import MiniSearch from 'minisearch';
|
|
4
5
|
|
|
5
6
|
// src/db/schema.ts
|
|
6
7
|
async function setupDatabase(db, prefix) {
|
|
@@ -2803,6 +2804,195 @@ function mapRowToEdge(row) {
|
|
|
2803
2804
|
created_at: Number(row.created_at)
|
|
2804
2805
|
};
|
|
2805
2806
|
}
|
|
2807
|
+
var MiniSearchIndexStrategy = class {
|
|
2808
|
+
constructor() {
|
|
2809
|
+
/**
|
|
2810
|
+
* Per-entity id set. `replaceEntity()` and `hasIndexedEntity()` read it;
|
|
2811
|
+
* `addDocuments()` and `discardIds()` keep it in sync. `SearchService` does
|
|
2812
|
+
* not see this map; it only sees `hasIndexedEntity`. The previous
|
|
2813
|
+
* `miniSearchEntryIdsByEntity` lived on `SearchService`.
|
|
2814
|
+
*/
|
|
2815
|
+
this.entryIdsByEntity = /* @__PURE__ */ new Map();
|
|
2816
|
+
/**
|
|
2817
|
+
* Reverse of `entryIdsByEntity`. `replace(entityId, ids, …)` uses it to
|
|
2818
|
+
* decide which ids are owned by `entityId` (the only safe membership test
|
|
2819
|
+
* — a rebuild that failed between its discard pass and the set replacement
|
|
2820
|
+
* can leave the forward set claiming ids the MiniSearch instance does not
|
|
2821
|
+
* hold). The pre-extraction `syncEntries` deleted from one set per id, not
|
|
2822
|
+
* from every entity's set.
|
|
2823
|
+
*/
|
|
2824
|
+
this.entityIdById = /* @__PURE__ */ new Map();
|
|
2825
|
+
this.index = this.createIndex();
|
|
2826
|
+
}
|
|
2827
|
+
/**
|
|
2828
|
+
* Build a fresh MiniSearch instance with the project's index options.
|
|
2829
|
+
* Called from the constructor and from `replaceAll`. We do not use
|
|
2830
|
+
* `removeAll()` on an existing instance because that empties the index but
|
|
2831
|
+
* leaves `dirtCount` and the vacuum bookkeeping intact, which would trip
|
|
2832
|
+
* `syncEntries`' conditional vacuum early after a clear.
|
|
2833
|
+
*/
|
|
2834
|
+
createIndex() {
|
|
2835
|
+
return new MiniSearch({
|
|
2836
|
+
fields: ["title", "body", "tags"],
|
|
2837
|
+
storeFields: ["entity_id"],
|
|
2838
|
+
// Vacuuming is driven explicitly at the end of each serialized rebuild
|
|
2839
|
+
// (see SearchService.syncEntries). Auto-vacuum fires on its own
|
|
2840
|
+
// schedule, asynchronously with respect to the caller, and traversing
|
|
2841
|
+
// the tree mid-rebuild is what threw the uncaught TypeError in
|
|
2842
|
+
// MiniSearch.performVacuuming (#64).
|
|
2843
|
+
autoVacuum: false,
|
|
2844
|
+
searchOptions: {
|
|
2845
|
+
boost: { title: 2 },
|
|
2846
|
+
fuzzy: 0.2,
|
|
2847
|
+
prefix: true
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Incremental sync. Removes only ids currently tracked under `entityId`
|
|
2853
|
+
* (other entities' docs are untouched), then upserts `documents` by id.
|
|
2854
|
+
* The same id appearing in both `ids` and `documents` is discarded first
|
|
2855
|
+
* so the upsert does not throw on a duplicate id.
|
|
2856
|
+
*
|
|
2857
|
+
* Every document must carry `entityId` as its `entity_id`; a mismatch is
|
|
2858
|
+
* rejected before the write mutates anything. A tracking-only call
|
|
2859
|
+
* (`replace(entityId, [], [])`) registers the entity as indexed, matching
|
|
2860
|
+
* the interface contract.
|
|
2861
|
+
*
|
|
2862
|
+
* Atomicity: never awaits between the discard pass and `addDocuments`, so
|
|
2863
|
+
* a concurrent `search()` sees the index wholly before or wholly after,
|
|
2864
|
+
* never half-updated. This is the precondition the pre-extraction
|
|
2865
|
+
* `syncEntries` kept with its "no await from here to `addAll`" comment.
|
|
2866
|
+
*/
|
|
2867
|
+
async replace(entityId, ids, documents) {
|
|
2868
|
+
assertOwnedBy(entityId, documents);
|
|
2869
|
+
this.discardIds(ids, entityId);
|
|
2870
|
+
if (!this.entryIdsByEntity.has(entityId)) this.entryIdsByEntity.set(entityId, /* @__PURE__ */ new Set());
|
|
2871
|
+
this.addDocuments(documents);
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Per-entity rebuild. Removes every id tracked under `entityId` (with the
|
|
2875
|
+
* `index.has(id)` guard so a stale tracking set cannot make `discard()`
|
|
2876
|
+
* throw), then writes `documents`. The entity stays registered as
|
|
2877
|
+
* indexed — even when `documents` is empty — so the next `syncEntries`
|
|
2878
|
+
* call can take the fast path. Every document must belong to `entityId`.
|
|
2879
|
+
*/
|
|
2880
|
+
async replaceEntity(entityId, documents) {
|
|
2881
|
+
assertOwnedBy(entityId, documents);
|
|
2882
|
+
const previous = this.entryIdsByEntity.get(entityId);
|
|
2883
|
+
if (previous) this.discardIds([...previous]);
|
|
2884
|
+
this.entryIdsByEntity.set(entityId, /* @__PURE__ */ new Set());
|
|
2885
|
+
this.addDocuments(documents);
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Global rebuild / clear. Replaces the MiniSearch instance and clears both
|
|
2889
|
+
* id maps, so `hasIndexedEntity` returns `false` for every entity afterwards.
|
|
2890
|
+
* `SearchService.clearAll()` calls `replaceAll([])`.
|
|
2891
|
+
*/
|
|
2892
|
+
async replaceAll(documents) {
|
|
2893
|
+
this.index = this.createIndex();
|
|
2894
|
+
this.entryIdsByEntity.clear();
|
|
2895
|
+
this.entityIdById.clear();
|
|
2896
|
+
this.addDocuments(documents);
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Best-match-first search over the documents whose `entity_id` is in
|
|
2900
|
+
* `options.entityIds`. `preFilterLimit` overrides `limit` (hybrid retrieval
|
|
2901
|
+
* uses this to over-fetch candidates before the `limit` cap). Scores are
|
|
2902
|
+
* raw BM25+ — `SearchService.getKeywordScores` and `searchKeyword` apply
|
|
2903
|
+
* any scaling, not the strategy.
|
|
2904
|
+
*/
|
|
2905
|
+
async search(query, options) {
|
|
2906
|
+
if (options.entityIds.length === 0) return [];
|
|
2907
|
+
const entityIdSet = new Set(options.entityIds);
|
|
2908
|
+
const limit = options.preFilterLimit ?? options.limit;
|
|
2909
|
+
const raw = this.index.search(query, {
|
|
2910
|
+
filter: (r) => entityIdSet.has(r.entity_id),
|
|
2911
|
+
combineWith: "OR"
|
|
2912
|
+
});
|
|
2913
|
+
raw.sort((a, b) => {
|
|
2914
|
+
const scoreDiff = b.score - a.score;
|
|
2915
|
+
if (!Number.isNaN(scoreDiff) && scoreDiff !== 0) return scoreDiff;
|
|
2916
|
+
return a.id.localeCompare(b.id);
|
|
2917
|
+
});
|
|
2918
|
+
return raw.slice(0, limit).map((r) => ({
|
|
2919
|
+
id: r.id,
|
|
2920
|
+
entity_id: r.entity_id,
|
|
2921
|
+
score: r.score
|
|
2922
|
+
}));
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Explicit vacuum. MiniSearch 7.2.0's `dirtCount` rule: search after an
|
|
2926
|
+
* un-vacuumed discard inverts the relative scores of term-sharing
|
|
2927
|
+
* documents at any index size. `SearchService.syncEntries` calls this
|
|
2928
|
+
* after a discard-bearing turn. No-op when `dirtCount === 0`.
|
|
2929
|
+
*/
|
|
2930
|
+
async vacuum() {
|
|
2931
|
+
if (this.index.dirtCount > 0) {
|
|
2932
|
+
await this.index.vacuum();
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* `true` when `entityId` has been indexed at least once under the current
|
|
2937
|
+
* strategy state. Backed by `entryIdsByEntity.has(entityId)`, which is
|
|
2938
|
+
* kept in sync by `addDocuments`, `replaceEntity`, and `replaceAll`.
|
|
2939
|
+
*/
|
|
2940
|
+
hasIndexedEntity(entityId) {
|
|
2941
|
+
return this.entryIdsByEntity.has(entityId);
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Add `documents` to the MiniSearch instance and register them in
|
|
2945
|
+
* `entryIdsByEntity` and `entityIdById`. MiniSearch mutates documents
|
|
2946
|
+
* during `add`, so each doc is copied first. `add` throws on an id the
|
|
2947
|
+
* index already holds — `replace` and `replaceEntity` discard first for
|
|
2948
|
+
* that reason.
|
|
2949
|
+
*
|
|
2950
|
+
* Adds and registers one document at a time: if a duplicate id throws
|
|
2951
|
+
* part-way through, every document already in the index is tracked, so
|
|
2952
|
+
* the next rebuild can discard it instead of failing on it again. This is
|
|
2953
|
+
* the precondition the per-turn recovery path relies on (#258 review).
|
|
2954
|
+
*/
|
|
2955
|
+
addDocuments(documents) {
|
|
2956
|
+
for (const d of documents) {
|
|
2957
|
+
const doc = { ...d };
|
|
2958
|
+
this.index.add(doc);
|
|
2959
|
+
const set = this.entryIdsByEntity.get(doc.entity_id) ?? /* @__PURE__ */ new Set();
|
|
2960
|
+
set.add(doc.id);
|
|
2961
|
+
this.entryIdsByEntity.set(doc.entity_id, set);
|
|
2962
|
+
this.entityIdById.set(doc.id, doc.entity_id);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Discard `ids` from the MiniSearch instance and from both id maps, but
|
|
2967
|
+
* only for ids currently owned by `entityId` (the second argument). When
|
|
2968
|
+
* `entityId` is omitted (used by `replaceEntity` for the bulk rebuild
|
|
2969
|
+
* path), every id is discarded regardless of owning entity.
|
|
2970
|
+
*
|
|
2971
|
+
* `index.has(id)` is the membership test of record: a rebuild that failed
|
|
2972
|
+
* between its own discard pass and the set replacement can leave the
|
|
2973
|
+
* tracking set claiming ids the index no longer holds — `discard()` throws
|
|
2974
|
+
* on those, so we guard.
|
|
2975
|
+
*/
|
|
2976
|
+
discardIds(ids, entityId) {
|
|
2977
|
+
for (const id of ids) {
|
|
2978
|
+
const owning = this.entityIdById.get(id);
|
|
2979
|
+
if (owning === void 0) continue;
|
|
2980
|
+
if (entityId !== void 0 && owning !== entityId) continue;
|
|
2981
|
+
if (this.index.has(id)) this.index.discard(id);
|
|
2982
|
+
this.entryIdsByEntity.get(owning)?.delete(id);
|
|
2983
|
+
this.entityIdById.delete(id);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
};
|
|
2987
|
+
function assertOwnedBy(entityId, documents) {
|
|
2988
|
+
for (const doc of documents) {
|
|
2989
|
+
if (doc.entity_id !== entityId) {
|
|
2990
|
+
throw new Error(
|
|
2991
|
+
`IndexStrategy write for entity ${entityId} got document ${doc.id} under entity ${doc.entity_id}`
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2806
2996
|
|
|
2807
2997
|
// src/services/MaintenanceScheduler.ts
|
|
2808
2998
|
var MaintenanceScheduler = class {
|
|
@@ -3509,7 +3699,10 @@ var WikiMemory = class {
|
|
|
3509
3699
|
);
|
|
3510
3700
|
this.lintService = new LintService(this.lintRepo, this.ontologyService);
|
|
3511
3701
|
this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
|
|
3512
|
-
this.searchService = new SearchService(
|
|
3702
|
+
this.searchService = new SearchService(
|
|
3703
|
+
this.entryRepo,
|
|
3704
|
+
new MiniSearchIndexStrategy()
|
|
3705
|
+
);
|
|
3513
3706
|
this.temporalService = new TemporalService(this.db, this.options, this.entryRepo, this.searchService, this.embeddingService);
|
|
3514
3707
|
this.jobManager = new JobManager(this.prefix);
|
|
3515
3708
|
this.promptService = new PromptService(options.config?.prompts, resolveGrounding(options.config?.grounding));
|
|
@@ -3902,6 +4095,32 @@ var WikiMemory = class {
|
|
|
3902
4095
|
clearVectorCache() {
|
|
3903
4096
|
this.searchService.evictCache();
|
|
3904
4097
|
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Brings the keyword search index up to date with SQLite after a host
|
|
4100
|
+
* transaction that used `upsertGraph` has committed. Core never sees that
|
|
4101
|
+
* commit, so without this the nodes stay out of keyword, hybrid and
|
|
4102
|
+
* fallback retrieval until the entity's next core write.
|
|
4103
|
+
*
|
|
4104
|
+
* With `entityId`, rebuilds that entity if it is stale or has never been
|
|
4105
|
+
* indexed; otherwise it is a no-op that still waits for rebuilds already
|
|
4106
|
+
* queued. Without an argument, rebuilds every entity currently marked
|
|
4107
|
+
* stale; when none is, it resolves immediately and does not wait for rebuilds
|
|
4108
|
+
* already queued.
|
|
4109
|
+
*
|
|
4110
|
+
* Call only after the host's transaction commits: called inside it, the
|
|
4111
|
+
* rebuild may index rows that later roll back, or miss them and clear the
|
|
4112
|
+
* stale flag anyway. Serialized with core's internal index syncs and takes
|
|
4113
|
+
* no maintenance lock. Never rejects for a valid `entityId` — on failure the
|
|
4114
|
+
* entity stays stale and the next call retries it. Does not compute
|
|
4115
|
+
* embeddings; that remains the maintenance sweep's job.
|
|
4116
|
+
*
|
|
4117
|
+
* @throws {TypeError} when `entityId` is given but is not a non-empty string.
|
|
4118
|
+
*/
|
|
4119
|
+
async syncSearchIndex(entityId) {
|
|
4120
|
+
if (entityId === void 0) return this.searchService.syncStale();
|
|
4121
|
+
assertEntityId(entityId);
|
|
4122
|
+
return this.searchService.syncEntries(entityId, []);
|
|
4123
|
+
}
|
|
3905
4124
|
async exportDump(entityIds) {
|
|
3906
4125
|
return this.importExportService.exportDump(entityIds);
|
|
3907
4126
|
}
|
|
@@ -3948,9 +4167,11 @@ var WikiMemory = class {
|
|
|
3948
4167
|
* MUST be called from inside an open `db.withTransactionAsync` callback —
|
|
3949
4168
|
* the third argument is the caller's `tx`. The method does not open a
|
|
3950
4169
|
* nested transaction, does not acquire any lock, and does not perform
|
|
3951
|
-
* post-commit work (search sync, embedding, cache eviction).
|
|
3952
|
-
*
|
|
3953
|
-
* (`
|
|
4170
|
+
* post-commit work (search sync, embedding, cache eviction). After the
|
|
4171
|
+
* transaction commits, call `syncSearchIndex(entityId)` — or
|
|
4172
|
+
* `syncSearchIndex()` after writing several entities in one transaction —
|
|
4173
|
+
* to make the new nodes keyword-searchable. Embeddings still come from the maintenance
|
|
4174
|
+
* sweep (`runLibrarian` / `runHeal` / `runOntologyBackfill` / `runPrune`,
|
|
3954
4175
|
* scoped via `listEntityIds`).
|
|
3955
4176
|
*
|
|
3956
4177
|
* Contract (full pre-flight validation lives inside
|