@equationalapplications/core-llm-wiki 6.4.0 → 6.5.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 +15 -0
- package/dist/{chunk-DPFM33WV.mjs → chunk-YNFYSLR6.mjs} +110 -22
- package/dist/chunk-YNFYSLR6.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +192 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DWpAgNLt.d.mts → testing-BtsN1pTK.d.mts} +123 -15
- package/dist/{testing-DWpAgNLt.d.ts → testing-BtsN1pTK.d.ts} +123 -15
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +108 -20
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-DPFM33WV.mjs.map +0 -1
|
@@ -104,6 +104,26 @@ interface OntologyBackfillResult {
|
|
|
104
104
|
* Stamped with the recheck cooldown, so they reappear as `deferred` next pass. */
|
|
105
105
|
skipped: number;
|
|
106
106
|
}
|
|
107
|
+
/** Result of a single `runReembed` sweep. */
|
|
108
|
+
interface ReembedResult {
|
|
109
|
+
/** Facts successfully (re-)embedded this sweep. */
|
|
110
|
+
embedded: number;
|
|
111
|
+
/** Facts with a valid existing embedding left untouched because
|
|
112
|
+
* `skipExisting` was in effect (and no dimension mismatch forced a re-embed). */
|
|
113
|
+
skipped: number;
|
|
114
|
+
/** Facts whose embed attempt ran and failed this sweep. Includes
|
|
115
|
+
* `storage_error` failures, which are not backoff-tracked (a marker is
|
|
116
|
+
* itself a DB write — see the reembed README section). Convergence loops
|
|
117
|
+
* should test this counter, never `deferred`. */
|
|
118
|
+
failed: number;
|
|
119
|
+
/** Previously failed facts still inside their exponential backoff window
|
|
120
|
+
* (60s doubling, capped at 24h), not attempted this sweep. Not an error;
|
|
121
|
+
* a later sweep retries them once the window elapses. */
|
|
122
|
+
deferred: number;
|
|
123
|
+
/** Facts excluded for good: a `float32_overflow` failure or 5 failed
|
|
124
|
+
* lifetime attempts. `{ force: true }` retries these anyway. */
|
|
125
|
+
permanentlyFailed: number;
|
|
126
|
+
}
|
|
107
127
|
/**
|
|
108
128
|
* L3 heal-success record: a fact the model emitted a verdict for under a
|
|
109
129
|
* truncated view of its own body. `originalBodyChars` lets an operator flag
|
|
@@ -743,6 +763,14 @@ declare class PrunePartialFailureError extends Error {
|
|
|
743
763
|
constructor(deleted: number, failedAt: string, remaining: number, cause: Error, deletedTasks?: number, deletedEvents?: number);
|
|
744
764
|
}
|
|
745
765
|
declare const HOOK_TIMEOUT_MARKER: unique symbol;
|
|
766
|
+
/**
|
|
767
|
+
* Failure kinds eligible for a persisted embedding failure marker (spec §3.3).
|
|
768
|
+
* `no_provider` never marks (nothing to retry against), and `storage_error`
|
|
769
|
+
* never marks (D3 — a marker is itself a DB write). Defined here, neutral
|
|
770
|
+
* between repositories and services, so EntryRepository can type-narrow the
|
|
771
|
+
* marker kind without importing from services.
|
|
772
|
+
*/
|
|
773
|
+
type EmbeddingMarkerKind = 'invalid_vector' | 'float32_overflow' | 'provider_error';
|
|
746
774
|
/**
|
|
747
775
|
* Failure record for a single chunk that could not be ingested. Produced by
|
|
748
776
|
* `IngestionService.ingestDocument` and surfaced on the result's `parseFailures`
|
|
@@ -890,6 +918,18 @@ type EntryRowWithEmbeddings = EntryRowMetadata & {
|
|
|
890
918
|
embedding_blob: Uint8Array | null;
|
|
891
919
|
embedding: string | null;
|
|
892
920
|
};
|
|
921
|
+
/**
|
|
922
|
+
* One row of the reembed candidate scan. `findAllForReembed` does `SELECT *`,
|
|
923
|
+
* so the marker columns come back at runtime; naming them here makes that
|
|
924
|
+
* dependency visible to the compiler instead of hiding it behind a cast in
|
|
925
|
+
* MaintenanceService.runReembed (spec §4.4).
|
|
926
|
+
*/
|
|
927
|
+
type ReembedCandidateRow = WikiFact & {
|
|
928
|
+
embedding_blob?: Uint8Array | null;
|
|
929
|
+
embedding_failed_at?: number | null;
|
|
930
|
+
embedding_failure_kind?: string | null;
|
|
931
|
+
embedding_attempts?: number | null;
|
|
932
|
+
};
|
|
893
933
|
declare class EntryRepository extends BaseRepository {
|
|
894
934
|
private outbox;
|
|
895
935
|
private chunkSize;
|
|
@@ -918,6 +958,13 @@ declare class EntryRepository extends BaseRepository {
|
|
|
918
958
|
* New-row INSERTs still bind a value so the column is populated; if the
|
|
919
959
|
* caller supplied nothing, the schema DEFAULT 'stable' applies when the
|
|
920
960
|
* bind is bound as 'stable' explicitly here.
|
|
961
|
+
*
|
|
962
|
+
* Marker discipline (spec §4.1): a conflict carrying a real
|
|
963
|
+
* `embedding_blob` clears the embedding failure markers, because the row now
|
|
964
|
+
* HAS a valid embedding and a surviving marker would be a self-contradictory
|
|
965
|
+
* diagnostic that inflates runReembed's permanentlyFailed counter. A conflict
|
|
966
|
+
* with no blob leaves marker state alone, matching the "absent means don't
|
|
967
|
+
* touch" semantics used for embedding_blob itself.
|
|
921
968
|
*/
|
|
922
969
|
upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
|
|
923
970
|
changes: number;
|
|
@@ -1066,11 +1113,31 @@ declare class EntryRepository extends BaseRepository {
|
|
|
1066
1113
|
tags: string;
|
|
1067
1114
|
}>>;
|
|
1068
1115
|
updateEmbeddingBlob(id: string, blob: Uint8Array, tx?: SQLiteAdapter): Promise<void>;
|
|
1116
|
+
/**
|
|
1117
|
+
* Record a failed embedding attempt. Deliberately does NOT touch updated_at
|
|
1118
|
+
* (import merge is last-write-wins on it) and pushes no outbox event —
|
|
1119
|
+
* embedding lifecycle is local state, not replicated. Same discipline as
|
|
1120
|
+
* updateEmbeddingBlob. See spec §3.5.
|
|
1121
|
+
*
|
|
1122
|
+
* Only marker-eligible kinds (spec §3.3) are accepted: `no_provider` never
|
|
1123
|
+
* marks and `storage_error` never marks, so retry/permanent-failure policy
|
|
1124
|
+
* built on these rows only ever sees real embed failures.
|
|
1125
|
+
*/
|
|
1126
|
+
markEmbeddingFailure(id: string, kind: EmbeddingMarkerKind, now: number, tx?: SQLiteAdapter): Promise<void>;
|
|
1127
|
+
/**
|
|
1128
|
+
* Clear marker state for every marked row. Called when the embedding
|
|
1129
|
+
* dimension is promoted: a new model produces different vectors, so past
|
|
1130
|
+
* failures — including `float32_overflow`, which is otherwise terminal —
|
|
1131
|
+
* no longer predict future ones (spec §2.3).
|
|
1132
|
+
*
|
|
1133
|
+
* Same discipline as markEmbeddingFailure: no `updated_at` touch and no
|
|
1134
|
+
* outbox event, because embedding lifecycle is local state, not replicated.
|
|
1135
|
+
* Returns the number of rows cleared.
|
|
1136
|
+
*/
|
|
1137
|
+
clearEmbeddingFailureMarkers(tx?: SQLiteAdapter): Promise<number>;
|
|
1069
1138
|
hasLegacySourceTypes(tx?: SQLiteAdapter): Promise<boolean>;
|
|
1070
1139
|
countLegacySourceTypes(tx?: SQLiteAdapter): Promise<number>;
|
|
1071
|
-
findAllForReembed(entityId?: string, tx?: SQLiteAdapter): Promise<
|
|
1072
|
-
embedding_blob?: Uint8Array | null;
|
|
1073
|
-
}>>;
|
|
1140
|
+
findAllForReembed(entityId?: string, tx?: SQLiteAdapter): Promise<ReembedCandidateRow[]>;
|
|
1074
1141
|
findRowsForSourceRefMigration(tx?: SQLiteAdapter): Promise<Array<{
|
|
1075
1142
|
rowid: number;
|
|
1076
1143
|
source_ref: string;
|
|
@@ -1540,6 +1607,14 @@ declare class EdgeRepository extends BaseRepository {
|
|
|
1540
1607
|
}>;
|
|
1541
1608
|
}
|
|
1542
1609
|
|
|
1610
|
+
type EmbedFailureKind = EmbeddingMarkerKind | 'no_provider' | 'storage_error';
|
|
1611
|
+
type EmbedFactResult = {
|
|
1612
|
+
ok: true;
|
|
1613
|
+
dimension: number;
|
|
1614
|
+
} | {
|
|
1615
|
+
ok: false;
|
|
1616
|
+
kind: EmbedFailureKind;
|
|
1617
|
+
};
|
|
1543
1618
|
declare class EmbeddingService {
|
|
1544
1619
|
private db;
|
|
1545
1620
|
private options;
|
|
@@ -1547,8 +1622,36 @@ declare class EmbeddingService {
|
|
|
1547
1622
|
private metadataRepo;
|
|
1548
1623
|
constructor(db: SQLiteAdapter, options: WikiOptions, entryRepo: EntryRepository, metadataRepo: MetadataRepository);
|
|
1549
1624
|
storeEmbeddingDimension(dim: number): Promise<void>;
|
|
1550
|
-
/**
|
|
1625
|
+
/**
|
|
1626
|
+
* Promotes embedding_dimension_mismatch to canonical embedding_dimension when
|
|
1627
|
+
* safe, and clears embedding failure markers as part of the same event.
|
|
1628
|
+
*
|
|
1629
|
+
* Marker reset (spec §2.3): a promoted dimension means a different model is
|
|
1630
|
+
* producing the vectors, so prior failures no longer predict future ones.
|
|
1631
|
+
* `float32_overflow` is cleared too — it is terminal only because retrying is
|
|
1632
|
+
* the same arithmetic on the same vector, and after a model change it is not.
|
|
1633
|
+
*
|
|
1634
|
+
* All three writes commit atomically; see the transaction note inline.
|
|
1635
|
+
*
|
|
1636
|
+
* Revived rows are NOT embedded here. They become eligible and are picked up
|
|
1637
|
+
* by the NEXT sweep, because runReembed calls this after its candidates were
|
|
1638
|
+
* already classified; same-sweep revival would be re-entrant.
|
|
1639
|
+
*/
|
|
1551
1640
|
reconcileEmbeddingDimension(): Promise<void>;
|
|
1641
|
+
tryEmbedFact(fact: {
|
|
1642
|
+
id: string;
|
|
1643
|
+
entity_id: string;
|
|
1644
|
+
title: string;
|
|
1645
|
+
body: string;
|
|
1646
|
+
tags: string | string[];
|
|
1647
|
+
}): Promise<EmbedFactResult>;
|
|
1648
|
+
/** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
|
|
1649
|
+
private markFailure;
|
|
1650
|
+
/**
|
|
1651
|
+
* Back-compatible boolean form. Kept because callers such as
|
|
1652
|
+
* ImportExportService branch on `if (!embedded)`; a discriminated result
|
|
1653
|
+
* object is always truthy and would silently disable that branch.
|
|
1654
|
+
*/
|
|
1552
1655
|
embedFact(fact: {
|
|
1553
1656
|
id: string;
|
|
1554
1657
|
entity_id: string;
|
|
@@ -1911,14 +2014,23 @@ declare class MaintenanceService {
|
|
|
1911
2014
|
promptOverride?: string;
|
|
1912
2015
|
batchSize?: number;
|
|
1913
2016
|
}): Promise<OntologyBackfillResult>;
|
|
2017
|
+
/**
|
|
2018
|
+
* Re-embed facts, honouring per-row failure markers and exponential backoff.
|
|
2019
|
+
*
|
|
2020
|
+
* Marker lifecycle (spec §2): markers are cleared by a successful embed, by
|
|
2021
|
+
* an upsert carrying a valid blob, and by an embedding-dimension promotion.
|
|
2022
|
+
* A promotion revives rows for the NEXT sweep, not this one — reconciliation
|
|
2023
|
+
* runs after candidates were already classified.
|
|
2024
|
+
*
|
|
2025
|
+
* Residual, by design: swapping to a provider with the SAME dimension never
|
|
2026
|
+
* sets embedding_dimension_mismatch, so promotion never fires and markers
|
|
2027
|
+
* survive the swap. Use `runReembed({ force: true })` to clear that state —
|
|
2028
|
+
* `force` bypasses classification entirely and retries every row.
|
|
2029
|
+
*/
|
|
1914
2030
|
runReembed(entityId?: string, opts?: {
|
|
1915
2031
|
force?: boolean;
|
|
1916
2032
|
skipExisting?: boolean;
|
|
1917
|
-
}): Promise<
|
|
1918
|
-
embedded: number;
|
|
1919
|
-
skipped: number;
|
|
1920
|
-
failed: number;
|
|
1921
|
-
}>;
|
|
2033
|
+
}): Promise<ReembedResult>;
|
|
1922
2034
|
forget(entityId: string, params: {
|
|
1923
2035
|
entryId?: string;
|
|
1924
2036
|
taskId?: string;
|
|
@@ -2275,11 +2387,7 @@ declare class WikiMemory {
|
|
|
2275
2387
|
runReembed(entityId?: string, opts?: {
|
|
2276
2388
|
force?: boolean;
|
|
2277
2389
|
skipExisting?: boolean;
|
|
2278
|
-
}): Promise<
|
|
2279
|
-
embedded: number;
|
|
2280
|
-
skipped: number;
|
|
2281
|
-
failed: number;
|
|
2282
|
-
}>;
|
|
2390
|
+
}): Promise<ReembedResult>;
|
|
2283
2391
|
getEntityStatus(entityId: string): EntityStatus;
|
|
2284
2392
|
subscribeEntityStatus(entityId: string, callback: (status: EntityStatus) => void): () => void;
|
|
2285
2393
|
clearVectorCache(): void;
|
|
@@ -2458,4 +2566,4 @@ declare class WikiMemory {
|
|
|
2458
2566
|
setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
|
|
2459
2567
|
}
|
|
2460
2568
|
|
|
2461
|
-
export {
|
|
2569
|
+
export { WikiIngestEmptyError as $, type ReembedResult as A, type VectorRankerFallback as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerRankArgs as J, type VectorRankerSemanticResult as K, type LLMProvider as L, type MemoryBundle as M, WikiBusyError as N, type OntologyManifest as O, type PromptOverrides as P, type WikiBusyOperation as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiCheckpoint as T, type WikiConfig as U, type VectorRanker as V, type WikiOptions as W, WikiDuplicateHashError as X, type WikiEdge as Y, type WikiEvent as Z, type WikiFact as _, type MemoryDump as a, WikiInvalidReadOptions as a0, type WikiMemoryTestAccess as a1, type WikiOutboxEvent as a2, WikiParseError as a3, WikiSourceRefHashCollision as a4, WikiStrictOntologyViolation as a5, type WikiTask as a6, WikiTransactionError as a7, EmbeddingService as a8, ImportExportService as a9, IngestionService as aa, JobManager as ab, MaintenanceService as ac, RetrievalService as ad, SearchService as ae, WriteService as af, type FormattedMemoryDump as b, WikiMemory as c, type EmbedFailureKind as d, type EmbeddingMarkerKind as e, type EntityStatus as f, type ExtractedFact as g, type ExtractedFactEdge as h, type ExtractedFactWithOntology as i, type ExtractedTask as j, type GraphTraversalOptions as k, HEAL_RECHECK_MS as l, HOOK_TIMEOUT_MARKER as m, type HealResult as n, ONTOLOGY_BACKFILL_BATCH_SIZE as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyMode as u, type OntologyNodeType as v, type OntologyPromptContext as w, type OntologyUpdates as x, PromptService as y, PrunePartialFailureError as z };
|
|
@@ -104,6 +104,26 @@ interface OntologyBackfillResult {
|
|
|
104
104
|
* Stamped with the recheck cooldown, so they reappear as `deferred` next pass. */
|
|
105
105
|
skipped: number;
|
|
106
106
|
}
|
|
107
|
+
/** Result of a single `runReembed` sweep. */
|
|
108
|
+
interface ReembedResult {
|
|
109
|
+
/** Facts successfully (re-)embedded this sweep. */
|
|
110
|
+
embedded: number;
|
|
111
|
+
/** Facts with a valid existing embedding left untouched because
|
|
112
|
+
* `skipExisting` was in effect (and no dimension mismatch forced a re-embed). */
|
|
113
|
+
skipped: number;
|
|
114
|
+
/** Facts whose embed attempt ran and failed this sweep. Includes
|
|
115
|
+
* `storage_error` failures, which are not backoff-tracked (a marker is
|
|
116
|
+
* itself a DB write — see the reembed README section). Convergence loops
|
|
117
|
+
* should test this counter, never `deferred`. */
|
|
118
|
+
failed: number;
|
|
119
|
+
/** Previously failed facts still inside their exponential backoff window
|
|
120
|
+
* (60s doubling, capped at 24h), not attempted this sweep. Not an error;
|
|
121
|
+
* a later sweep retries them once the window elapses. */
|
|
122
|
+
deferred: number;
|
|
123
|
+
/** Facts excluded for good: a `float32_overflow` failure or 5 failed
|
|
124
|
+
* lifetime attempts. `{ force: true }` retries these anyway. */
|
|
125
|
+
permanentlyFailed: number;
|
|
126
|
+
}
|
|
107
127
|
/**
|
|
108
128
|
* L3 heal-success record: a fact the model emitted a verdict for under a
|
|
109
129
|
* truncated view of its own body. `originalBodyChars` lets an operator flag
|
|
@@ -743,6 +763,14 @@ declare class PrunePartialFailureError extends Error {
|
|
|
743
763
|
constructor(deleted: number, failedAt: string, remaining: number, cause: Error, deletedTasks?: number, deletedEvents?: number);
|
|
744
764
|
}
|
|
745
765
|
declare const HOOK_TIMEOUT_MARKER: unique symbol;
|
|
766
|
+
/**
|
|
767
|
+
* Failure kinds eligible for a persisted embedding failure marker (spec §3.3).
|
|
768
|
+
* `no_provider` never marks (nothing to retry against), and `storage_error`
|
|
769
|
+
* never marks (D3 — a marker is itself a DB write). Defined here, neutral
|
|
770
|
+
* between repositories and services, so EntryRepository can type-narrow the
|
|
771
|
+
* marker kind without importing from services.
|
|
772
|
+
*/
|
|
773
|
+
type EmbeddingMarkerKind = 'invalid_vector' | 'float32_overflow' | 'provider_error';
|
|
746
774
|
/**
|
|
747
775
|
* Failure record for a single chunk that could not be ingested. Produced by
|
|
748
776
|
* `IngestionService.ingestDocument` and surfaced on the result's `parseFailures`
|
|
@@ -890,6 +918,18 @@ type EntryRowWithEmbeddings = EntryRowMetadata & {
|
|
|
890
918
|
embedding_blob: Uint8Array | null;
|
|
891
919
|
embedding: string | null;
|
|
892
920
|
};
|
|
921
|
+
/**
|
|
922
|
+
* One row of the reembed candidate scan. `findAllForReembed` does `SELECT *`,
|
|
923
|
+
* so the marker columns come back at runtime; naming them here makes that
|
|
924
|
+
* dependency visible to the compiler instead of hiding it behind a cast in
|
|
925
|
+
* MaintenanceService.runReembed (spec §4.4).
|
|
926
|
+
*/
|
|
927
|
+
type ReembedCandidateRow = WikiFact & {
|
|
928
|
+
embedding_blob?: Uint8Array | null;
|
|
929
|
+
embedding_failed_at?: number | null;
|
|
930
|
+
embedding_failure_kind?: string | null;
|
|
931
|
+
embedding_attempts?: number | null;
|
|
932
|
+
};
|
|
893
933
|
declare class EntryRepository extends BaseRepository {
|
|
894
934
|
private outbox;
|
|
895
935
|
private chunkSize;
|
|
@@ -918,6 +958,13 @@ declare class EntryRepository extends BaseRepository {
|
|
|
918
958
|
* New-row INSERTs still bind a value so the column is populated; if the
|
|
919
959
|
* caller supplied nothing, the schema DEFAULT 'stable' applies when the
|
|
920
960
|
* bind is bound as 'stable' explicitly here.
|
|
961
|
+
*
|
|
962
|
+
* Marker discipline (spec §4.1): a conflict carrying a real
|
|
963
|
+
* `embedding_blob` clears the embedding failure markers, because the row now
|
|
964
|
+
* HAS a valid embedding and a surviving marker would be a self-contradictory
|
|
965
|
+
* diagnostic that inflates runReembed's permanentlyFailed counter. A conflict
|
|
966
|
+
* with no blob leaves marker state alone, matching the "absent means don't
|
|
967
|
+
* touch" semantics used for embedding_blob itself.
|
|
921
968
|
*/
|
|
922
969
|
upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
|
|
923
970
|
changes: number;
|
|
@@ -1066,11 +1113,31 @@ declare class EntryRepository extends BaseRepository {
|
|
|
1066
1113
|
tags: string;
|
|
1067
1114
|
}>>;
|
|
1068
1115
|
updateEmbeddingBlob(id: string, blob: Uint8Array, tx?: SQLiteAdapter): Promise<void>;
|
|
1116
|
+
/**
|
|
1117
|
+
* Record a failed embedding attempt. Deliberately does NOT touch updated_at
|
|
1118
|
+
* (import merge is last-write-wins on it) and pushes no outbox event —
|
|
1119
|
+
* embedding lifecycle is local state, not replicated. Same discipline as
|
|
1120
|
+
* updateEmbeddingBlob. See spec §3.5.
|
|
1121
|
+
*
|
|
1122
|
+
* Only marker-eligible kinds (spec §3.3) are accepted: `no_provider` never
|
|
1123
|
+
* marks and `storage_error` never marks, so retry/permanent-failure policy
|
|
1124
|
+
* built on these rows only ever sees real embed failures.
|
|
1125
|
+
*/
|
|
1126
|
+
markEmbeddingFailure(id: string, kind: EmbeddingMarkerKind, now: number, tx?: SQLiteAdapter): Promise<void>;
|
|
1127
|
+
/**
|
|
1128
|
+
* Clear marker state for every marked row. Called when the embedding
|
|
1129
|
+
* dimension is promoted: a new model produces different vectors, so past
|
|
1130
|
+
* failures — including `float32_overflow`, which is otherwise terminal —
|
|
1131
|
+
* no longer predict future ones (spec §2.3).
|
|
1132
|
+
*
|
|
1133
|
+
* Same discipline as markEmbeddingFailure: no `updated_at` touch and no
|
|
1134
|
+
* outbox event, because embedding lifecycle is local state, not replicated.
|
|
1135
|
+
* Returns the number of rows cleared.
|
|
1136
|
+
*/
|
|
1137
|
+
clearEmbeddingFailureMarkers(tx?: SQLiteAdapter): Promise<number>;
|
|
1069
1138
|
hasLegacySourceTypes(tx?: SQLiteAdapter): Promise<boolean>;
|
|
1070
1139
|
countLegacySourceTypes(tx?: SQLiteAdapter): Promise<number>;
|
|
1071
|
-
findAllForReembed(entityId?: string, tx?: SQLiteAdapter): Promise<
|
|
1072
|
-
embedding_blob?: Uint8Array | null;
|
|
1073
|
-
}>>;
|
|
1140
|
+
findAllForReembed(entityId?: string, tx?: SQLiteAdapter): Promise<ReembedCandidateRow[]>;
|
|
1074
1141
|
findRowsForSourceRefMigration(tx?: SQLiteAdapter): Promise<Array<{
|
|
1075
1142
|
rowid: number;
|
|
1076
1143
|
source_ref: string;
|
|
@@ -1540,6 +1607,14 @@ declare class EdgeRepository extends BaseRepository {
|
|
|
1540
1607
|
}>;
|
|
1541
1608
|
}
|
|
1542
1609
|
|
|
1610
|
+
type EmbedFailureKind = EmbeddingMarkerKind | 'no_provider' | 'storage_error';
|
|
1611
|
+
type EmbedFactResult = {
|
|
1612
|
+
ok: true;
|
|
1613
|
+
dimension: number;
|
|
1614
|
+
} | {
|
|
1615
|
+
ok: false;
|
|
1616
|
+
kind: EmbedFailureKind;
|
|
1617
|
+
};
|
|
1543
1618
|
declare class EmbeddingService {
|
|
1544
1619
|
private db;
|
|
1545
1620
|
private options;
|
|
@@ -1547,8 +1622,36 @@ declare class EmbeddingService {
|
|
|
1547
1622
|
private metadataRepo;
|
|
1548
1623
|
constructor(db: SQLiteAdapter, options: WikiOptions, entryRepo: EntryRepository, metadataRepo: MetadataRepository);
|
|
1549
1624
|
storeEmbeddingDimension(dim: number): Promise<void>;
|
|
1550
|
-
/**
|
|
1625
|
+
/**
|
|
1626
|
+
* Promotes embedding_dimension_mismatch to canonical embedding_dimension when
|
|
1627
|
+
* safe, and clears embedding failure markers as part of the same event.
|
|
1628
|
+
*
|
|
1629
|
+
* Marker reset (spec §2.3): a promoted dimension means a different model is
|
|
1630
|
+
* producing the vectors, so prior failures no longer predict future ones.
|
|
1631
|
+
* `float32_overflow` is cleared too — it is terminal only because retrying is
|
|
1632
|
+
* the same arithmetic on the same vector, and after a model change it is not.
|
|
1633
|
+
*
|
|
1634
|
+
* All three writes commit atomically; see the transaction note inline.
|
|
1635
|
+
*
|
|
1636
|
+
* Revived rows are NOT embedded here. They become eligible and are picked up
|
|
1637
|
+
* by the NEXT sweep, because runReembed calls this after its candidates were
|
|
1638
|
+
* already classified; same-sweep revival would be re-entrant.
|
|
1639
|
+
*/
|
|
1551
1640
|
reconcileEmbeddingDimension(): Promise<void>;
|
|
1641
|
+
tryEmbedFact(fact: {
|
|
1642
|
+
id: string;
|
|
1643
|
+
entity_id: string;
|
|
1644
|
+
title: string;
|
|
1645
|
+
body: string;
|
|
1646
|
+
tags: string | string[];
|
|
1647
|
+
}): Promise<EmbedFactResult>;
|
|
1648
|
+
/** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
|
|
1649
|
+
private markFailure;
|
|
1650
|
+
/**
|
|
1651
|
+
* Back-compatible boolean form. Kept because callers such as
|
|
1652
|
+
* ImportExportService branch on `if (!embedded)`; a discriminated result
|
|
1653
|
+
* object is always truthy and would silently disable that branch.
|
|
1654
|
+
*/
|
|
1552
1655
|
embedFact(fact: {
|
|
1553
1656
|
id: string;
|
|
1554
1657
|
entity_id: string;
|
|
@@ -1911,14 +2014,23 @@ declare class MaintenanceService {
|
|
|
1911
2014
|
promptOverride?: string;
|
|
1912
2015
|
batchSize?: number;
|
|
1913
2016
|
}): Promise<OntologyBackfillResult>;
|
|
2017
|
+
/**
|
|
2018
|
+
* Re-embed facts, honouring per-row failure markers and exponential backoff.
|
|
2019
|
+
*
|
|
2020
|
+
* Marker lifecycle (spec §2): markers are cleared by a successful embed, by
|
|
2021
|
+
* an upsert carrying a valid blob, and by an embedding-dimension promotion.
|
|
2022
|
+
* A promotion revives rows for the NEXT sweep, not this one — reconciliation
|
|
2023
|
+
* runs after candidates were already classified.
|
|
2024
|
+
*
|
|
2025
|
+
* Residual, by design: swapping to a provider with the SAME dimension never
|
|
2026
|
+
* sets embedding_dimension_mismatch, so promotion never fires and markers
|
|
2027
|
+
* survive the swap. Use `runReembed({ force: true })` to clear that state —
|
|
2028
|
+
* `force` bypasses classification entirely and retries every row.
|
|
2029
|
+
*/
|
|
1914
2030
|
runReembed(entityId?: string, opts?: {
|
|
1915
2031
|
force?: boolean;
|
|
1916
2032
|
skipExisting?: boolean;
|
|
1917
|
-
}): Promise<
|
|
1918
|
-
embedded: number;
|
|
1919
|
-
skipped: number;
|
|
1920
|
-
failed: number;
|
|
1921
|
-
}>;
|
|
2033
|
+
}): Promise<ReembedResult>;
|
|
1922
2034
|
forget(entityId: string, params: {
|
|
1923
2035
|
entryId?: string;
|
|
1924
2036
|
taskId?: string;
|
|
@@ -2275,11 +2387,7 @@ declare class WikiMemory {
|
|
|
2275
2387
|
runReembed(entityId?: string, opts?: {
|
|
2276
2388
|
force?: boolean;
|
|
2277
2389
|
skipExisting?: boolean;
|
|
2278
|
-
}): Promise<
|
|
2279
|
-
embedded: number;
|
|
2280
|
-
skipped: number;
|
|
2281
|
-
failed: number;
|
|
2282
|
-
}>;
|
|
2390
|
+
}): Promise<ReembedResult>;
|
|
2283
2391
|
getEntityStatus(entityId: string): EntityStatus;
|
|
2284
2392
|
subscribeEntityStatus(entityId: string, callback: (status: EntityStatus) => void): () => void;
|
|
2285
2393
|
clearVectorCache(): void;
|
|
@@ -2458,4 +2566,4 @@ declare class WikiMemory {
|
|
|
2458
2566
|
setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
|
|
2459
2567
|
}
|
|
2460
2568
|
|
|
2461
|
-
export {
|
|
2569
|
+
export { WikiIngestEmptyError as $, type ReembedResult as A, type VectorRankerFallback as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerRankArgs as J, type VectorRankerSemanticResult as K, type LLMProvider as L, type MemoryBundle as M, WikiBusyError as N, type OntologyManifest as O, type PromptOverrides as P, type WikiBusyOperation as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiCheckpoint as T, type WikiConfig as U, type VectorRanker as V, type WikiOptions as W, WikiDuplicateHashError as X, type WikiEdge as Y, type WikiEvent as Z, type WikiFact as _, type MemoryDump as a, WikiInvalidReadOptions as a0, type WikiMemoryTestAccess as a1, type WikiOutboxEvent as a2, WikiParseError as a3, WikiSourceRefHashCollision as a4, WikiStrictOntologyViolation as a5, type WikiTask as a6, WikiTransactionError as a7, EmbeddingService as a8, ImportExportService as a9, IngestionService as aa, JobManager as ab, MaintenanceService as ac, RetrievalService as ad, SearchService as ae, WriteService as af, type FormattedMemoryDump as b, WikiMemory as c, type EmbedFailureKind as d, type EmbeddingMarkerKind as e, type EntityStatus as f, type ExtractedFact as g, type ExtractedFactEdge as h, type ExtractedFactWithOntology as i, type ExtractedTask as j, type GraphTraversalOptions as k, HEAL_RECHECK_MS as l, HOOK_TIMEOUT_MARKER as m, type HealResult as n, ONTOLOGY_BACKFILL_BATCH_SIZE as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyMode as u, type OntologyNodeType as v, type OntologyPromptContext as w, type OntologyUpdates as x, PromptService as y, PrunePartialFailureError as z };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-BtsN1pTK.mjs';
|
|
2
2
|
import '@equationalapplications/core-okf';
|
|
3
3
|
import 'minisearch';
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-BtsN1pTK.js';
|
|
2
2
|
import '@equationalapplications/core-okf';
|
|
3
3
|
import 'minisearch';
|
package/dist/testing.js
CHANGED
|
@@ -651,20 +651,37 @@ var EmbeddingService = class {
|
|
|
651
651
|
await this.metadataRepo.setMeta("embedding_dimension", String(dim), this.db);
|
|
652
652
|
}
|
|
653
653
|
}
|
|
654
|
-
/**
|
|
654
|
+
/**
|
|
655
|
+
* Promotes embedding_dimension_mismatch to canonical embedding_dimension when
|
|
656
|
+
* safe, and clears embedding failure markers as part of the same event.
|
|
657
|
+
*
|
|
658
|
+
* Marker reset (spec §2.3): a promoted dimension means a different model is
|
|
659
|
+
* producing the vectors, so prior failures no longer predict future ones.
|
|
660
|
+
* `float32_overflow` is cleared too — it is terminal only because retrying is
|
|
661
|
+
* the same arithmetic on the same vector, and after a model change it is not.
|
|
662
|
+
*
|
|
663
|
+
* All three writes commit atomically; see the transaction note inline.
|
|
664
|
+
*
|
|
665
|
+
* Revived rows are NOT embedded here. They become eligible and are picked up
|
|
666
|
+
* by the NEXT sweep, because runReembed calls this after its candidates were
|
|
667
|
+
* already classified; same-sweep revival would be re-entrant.
|
|
668
|
+
*/
|
|
655
669
|
async reconcileEmbeddingDimension() {
|
|
656
670
|
const mismatchValue = await this.metadataRepo.getMeta("embedding_dimension_mismatch");
|
|
657
671
|
if (!mismatchValue) return;
|
|
658
672
|
const newDim = parseInt(mismatchValue, 10);
|
|
659
673
|
const residualCount = await this.entryRepo.countStaleEmbeddings(newDim);
|
|
660
674
|
if (residualCount === 0) {
|
|
661
|
-
await this.
|
|
662
|
-
|
|
675
|
+
await this.db.withTransactionAsync(async (tx) => {
|
|
676
|
+
await this.metadataRepo.setMeta("embedding_dimension", mismatchValue, tx);
|
|
677
|
+
await this.metadataRepo.clearDimensionMismatch(tx);
|
|
678
|
+
await this.entryRepo.clearEmbeddingFailureMarkers(tx);
|
|
679
|
+
});
|
|
663
680
|
}
|
|
664
681
|
}
|
|
665
|
-
async
|
|
682
|
+
async tryEmbedFact(fact) {
|
|
666
683
|
const embedFn = this.options.llmProvider.embed;
|
|
667
|
-
if (
|
|
684
|
+
if (typeof embedFn !== "function") return { ok: false, kind: "no_provider" };
|
|
668
685
|
let tagsStr;
|
|
669
686
|
if (Array.isArray(fact.tags)) {
|
|
670
687
|
tagsStr = fact.tags.join(" ");
|
|
@@ -679,13 +696,15 @@ var EmbeddingService = class {
|
|
|
679
696
|
const configuredMaxEmbedChars = this.options.config?.maxEmbedChars;
|
|
680
697
|
const maxEmbedChars = Number.isFinite(configuredMaxEmbedChars) ? Math.min(Math.max(0, Math.trunc(configuredMaxEmbedChars)), EMBED_CHARS_CEILING) : DEFAULT_MAX_EMBED_CHARS;
|
|
681
698
|
const text = clip(`${fact.title} ${fact.body} ${tagsStr}`.trim(), maxEmbedChars);
|
|
699
|
+
let float32Vector;
|
|
682
700
|
try {
|
|
683
701
|
const vector = await embedFn(text);
|
|
684
702
|
if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
|
|
685
703
|
console.warn(`[WikiMemory] embedFact: embed() returned an invalid vector for ${fact.id}; skipping.`);
|
|
686
|
-
|
|
704
|
+
await this.markFailure(fact.id, "invalid_vector");
|
|
705
|
+
return { ok: false, kind: "invalid_vector" };
|
|
687
706
|
}
|
|
688
|
-
|
|
707
|
+
float32Vector = new Float32Array(vector);
|
|
689
708
|
let hasNonFinite = false;
|
|
690
709
|
for (let i = 0; i < float32Vector.length; i++) {
|
|
691
710
|
if (!isFinite(float32Vector[i])) {
|
|
@@ -695,22 +714,46 @@ var EmbeddingService = class {
|
|
|
695
714
|
}
|
|
696
715
|
if (hasNonFinite) {
|
|
697
716
|
console.warn(`[WikiMemory] embedFact: embed() returned values that overflow float32 for ${fact.id}; skipping.`);
|
|
698
|
-
|
|
717
|
+
await this.markFailure(fact.id, "float32_overflow");
|
|
718
|
+
return { ok: false, kind: "float32_overflow" };
|
|
699
719
|
}
|
|
720
|
+
} catch (err) {
|
|
721
|
+
console.warn(`[WikiMemory] embedFact failed for ${fact.id}:`, err);
|
|
722
|
+
await this.markFailure(fact.id, "provider_error");
|
|
723
|
+
return { ok: false, kind: "provider_error" };
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
700
726
|
await this.storeEmbeddingDimension(float32Vector.length);
|
|
701
727
|
const blob = new Uint8Array(float32Vector.buffer);
|
|
702
728
|
await this.entryRepo.updateEmbeddingBlob(fact.id, blob);
|
|
703
|
-
try {
|
|
704
|
-
await this.notifyEmbeddingPersisted(fact.entity_id, fact.id, float32Vector);
|
|
705
|
-
} catch (hookErr) {
|
|
706
|
-
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed for ${fact.id}:`, hookErr);
|
|
707
|
-
}
|
|
708
|
-
return true;
|
|
709
729
|
} catch (err) {
|
|
710
|
-
console.warn(`[WikiMemory] embedFact failed for ${fact.id}:`, err);
|
|
711
|
-
return false;
|
|
730
|
+
console.warn(`[WikiMemory] embedFact: persisting embedding failed for ${fact.id}:`, err);
|
|
731
|
+
return { ok: false, kind: "storage_error" };
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
await this.notifyEmbeddingPersisted(fact.entity_id, fact.id, float32Vector);
|
|
735
|
+
} catch (hookErr) {
|
|
736
|
+
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed for ${fact.id}:`, hookErr);
|
|
737
|
+
}
|
|
738
|
+
return { ok: true, dimension: float32Vector.length };
|
|
739
|
+
}
|
|
740
|
+
/** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
|
|
741
|
+
async markFailure(id, kind) {
|
|
742
|
+
try {
|
|
743
|
+
await this.entryRepo.markEmbeddingFailure(id, kind, Date.now());
|
|
744
|
+
} catch (err) {
|
|
745
|
+
console.warn(`[WikiMemory] failed to record embedding failure for ${id}:`, err);
|
|
712
746
|
}
|
|
713
747
|
}
|
|
748
|
+
/**
|
|
749
|
+
* Back-compatible boolean form. Kept because callers such as
|
|
750
|
+
* ImportExportService branch on `if (!embedded)`; a discriminated result
|
|
751
|
+
* object is always truthy and would silently disable that branch.
|
|
752
|
+
*/
|
|
753
|
+
async embedFact(fact) {
|
|
754
|
+
const result = await this.tryEmbedFact(fact);
|
|
755
|
+
return result.ok;
|
|
756
|
+
}
|
|
714
757
|
async notifyEmbeddingPersisted(entityId, factId, vector) {
|
|
715
758
|
if (!this.options.vectorRanker?.onEmbeddingPersisted) return;
|
|
716
759
|
const vectorCopy = vector ? vector.slice() : null;
|
|
@@ -2109,6 +2152,23 @@ var HEAL_ANCHOR_SEARCH_OVERFETCH = 4;
|
|
|
2109
2152
|
var HEAL_MAX_PROMPT_CHARS = 4e4;
|
|
2110
2153
|
var HEAL_BATCH_SIZE = 25;
|
|
2111
2154
|
var HEAL_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2155
|
+
var MAX_EMBED_ATTEMPTS = 5;
|
|
2156
|
+
var EMBED_RETRY_BASE_MS = 6e4;
|
|
2157
|
+
var EMBED_RETRY_CAP_MS = 24 * 60 * 60 * 1e3;
|
|
2158
|
+
function embedRetryDelayMs(attempts) {
|
|
2159
|
+
const n = Math.max(1, Math.trunc(attempts || 0));
|
|
2160
|
+
const delay = EMBED_RETRY_BASE_MS * Math.pow(2, n - 1);
|
|
2161
|
+
return Math.min(delay, EMBED_RETRY_CAP_MS);
|
|
2162
|
+
}
|
|
2163
|
+
function classifyReembedRow(row, now, force) {
|
|
2164
|
+
if (force) return "attempt";
|
|
2165
|
+
const failedAt = row.embedding_failed_at;
|
|
2166
|
+
if (failedAt === null || failedAt === void 0) return "attempt";
|
|
2167
|
+
if (row.embedding_failure_kind === "float32_overflow") return "permanent";
|
|
2168
|
+
const attempts = row.embedding_attempts ?? 0;
|
|
2169
|
+
if (attempts >= MAX_EMBED_ATTEMPTS) return "permanent";
|
|
2170
|
+
return now - failedAt >= embedRetryDelayMs(attempts) ? "attempt" : "defer";
|
|
2171
|
+
}
|
|
2112
2172
|
var SKIP_ERROR_LOG_CHARS = 4096;
|
|
2113
2173
|
var formatSkipError = (err) => {
|
|
2114
2174
|
let base;
|
|
@@ -2247,9 +2307,24 @@ var MaintenanceService = class {
|
|
|
2247
2307
|
this.jobManager.releaseLock("ontologyBackfill", entityId);
|
|
2248
2308
|
}
|
|
2249
2309
|
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Re-embed facts, honouring per-row failure markers and exponential backoff.
|
|
2312
|
+
*
|
|
2313
|
+
* Marker lifecycle (spec §2): markers are cleared by a successful embed, by
|
|
2314
|
+
* an upsert carrying a valid blob, and by an embedding-dimension promotion.
|
|
2315
|
+
* A promotion revives rows for the NEXT sweep, not this one — reconciliation
|
|
2316
|
+
* runs after candidates were already classified.
|
|
2317
|
+
*
|
|
2318
|
+
* Residual, by design: swapping to a provider with the SAME dimension never
|
|
2319
|
+
* sets embedding_dimension_mismatch, so promotion never fires and markers
|
|
2320
|
+
* survive the swap. Use `runReembed({ force: true })` to clear that state —
|
|
2321
|
+
* `force` bypasses classification entirely and retries every row.
|
|
2322
|
+
*/
|
|
2250
2323
|
async runReembed(entityId, opts) {
|
|
2251
2324
|
const embedFn = this.options.llmProvider.embed;
|
|
2252
|
-
if (
|
|
2325
|
+
if (typeof embedFn !== "function") {
|
|
2326
|
+
return { embedded: 0, skipped: 0, failed: 0, deferred: 0, permanentlyFailed: 0 };
|
|
2327
|
+
}
|
|
2253
2328
|
const op = entityId ? "reembed" : "global_reembed";
|
|
2254
2329
|
this.jobManager.acquireLock(op, entityId ?? "*");
|
|
2255
2330
|
try {
|
|
@@ -2272,6 +2347,10 @@ var MaintenanceService = class {
|
|
|
2272
2347
|
let embedded = 0;
|
|
2273
2348
|
let skipped = 0;
|
|
2274
2349
|
let failed = 0;
|
|
2350
|
+
let deferred = 0;
|
|
2351
|
+
let permanentlyFailed = 0;
|
|
2352
|
+
const force = opts?.force ?? false;
|
|
2353
|
+
const now = Date.now();
|
|
2275
2354
|
try {
|
|
2276
2355
|
for (const row of rows) {
|
|
2277
2356
|
const existingBlob = row.embedding_blob;
|
|
@@ -2283,8 +2362,17 @@ var MaintenanceService = class {
|
|
|
2283
2362
|
continue;
|
|
2284
2363
|
}
|
|
2285
2364
|
}
|
|
2286
|
-
const
|
|
2287
|
-
if (
|
|
2365
|
+
const disposition = classifyReembedRow(row, now, force);
|
|
2366
|
+
if (disposition === "defer") {
|
|
2367
|
+
deferred++;
|
|
2368
|
+
continue;
|
|
2369
|
+
}
|
|
2370
|
+
if (disposition === "permanent") {
|
|
2371
|
+
permanentlyFailed++;
|
|
2372
|
+
continue;
|
|
2373
|
+
}
|
|
2374
|
+
const result = await this.embeddingService.tryEmbedFact(row);
|
|
2375
|
+
if (result.ok) embedded++;
|
|
2288
2376
|
else failed++;
|
|
2289
2377
|
}
|
|
2290
2378
|
if (embedded > 0) {
|
|
@@ -2293,7 +2381,7 @@ var MaintenanceService = class {
|
|
|
2293
2381
|
} finally {
|
|
2294
2382
|
this.searchService.evictCache(entityId);
|
|
2295
2383
|
}
|
|
2296
|
-
return { embedded, skipped, failed };
|
|
2384
|
+
return { embedded, skipped, failed, deferred, permanentlyFailed };
|
|
2297
2385
|
} finally {
|
|
2298
2386
|
this.jobManager.releaseLock(op, entityId ?? "*");
|
|
2299
2387
|
}
|