@equationalapplications/core-llm-wiki 7.9.0 → 7.9.2
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/dist/{chunk-J2TWKOEE.mjs → chunk-7FRLQ7ZO.mjs} +73 -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 +264 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +196 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-Kb1uVj1E.d.mts → testing-Bf6etdUy.d.mts} +122 -23
- package/dist/{testing-Kb1uVj1E.d.ts → testing-Bf6etdUy.d.ts} +122 -23
- package/dist/testing.d.mts +1 -2
- package/dist/testing.d.ts +1 -2
- package/dist/testing.js +73 -131
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-J2TWKOEE.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));
|