rag-memory-epf-mcp 6.4.0 → 6.4.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 +42 -0
- package/dist/index.d.ts +51 -2
- package/dist/index.js +456 -149
- package/dist/src/chunkerC.d.ts +1 -0
- package/dist/src/chunkerC.js +69 -22
- package/dist/src/kg/relation-evidence.d.ts +156 -0
- package/dist/src/kg/relation-evidence.js +438 -0
- package/dist/src/observations/projection.d.ts +2 -0
- package/dist/src/observations/projection.js +49 -0
- package/dist/src/tools/knowledge-graph-tools.js +7 -3
- package/docs/UPDATING.md +10 -0
- package/package.json +2 -2
package/dist/src/chunkerC.d.ts
CHANGED
|
@@ -14,5 +14,6 @@ type Block = {
|
|
|
14
14
|
heading: boolean;
|
|
15
15
|
};
|
|
16
16
|
export declare function buildBlocks(lines: string[]): Block[];
|
|
17
|
+
export declare function splitOversize(block: string, enc: Tiktoken, maxTokens: number): string[];
|
|
17
18
|
export declare function chunkStructured(text: string, enc: Tiktoken, maxTokens?: number): CSegment[];
|
|
18
19
|
export {};
|
package/dist/src/chunkerC.js
CHANGED
|
@@ -8,13 +8,18 @@ export const LEGACY_SIGNATURE = 'legacy-unknown';
|
|
|
8
8
|
export function mergeMinTokens(maxTokens) {
|
|
9
9
|
return Math.floor(maxTokens / 2); // NOT >>1 — bitshift wraps at 2^31 (r12)
|
|
10
10
|
}
|
|
11
|
+
// kg-relation-evidence D3: fallback=cp-window-N names the windowed oversize split below. Boundaries
|
|
12
|
+
// differ from cp-exact only for blocks that reach the fallback, so the signature says so.
|
|
11
13
|
export function effectiveSignature(maxTokens) {
|
|
12
|
-
return `c1:enc=cl100k_base:max=${maxTokens}:overlap=0:fence=on:merge=${mergeMinTokens(maxTokens)}:fallback=cp-
|
|
14
|
+
return `c1:enc=cl100k_base:max=${maxTokens}:overlap=0:fence=on:merge=${mergeMinTokens(maxTokens)}:fallback=cp-window-${maxTokens}`;
|
|
13
15
|
}
|
|
14
16
|
// Strict parse (r5-15): the regex alone classified impossible signatures
|
|
15
17
|
// (max=0, max/fallback mismatch) as current. Cross-check the components.
|
|
18
|
+
// Both c1 fallbacks are the current FORMAT (stats "current" vs legacy/unknown, index.ts:4826-4831):
|
|
19
|
+
// a document stamped cp-exact by 6.4.0 is not re-chunked by an unchanged-content sync and must not be
|
|
20
|
+
// reported as an unknown format.
|
|
16
21
|
export function isCurrentFormatSignature(sig) {
|
|
17
|
-
const m = /^c1:enc=cl100k_base:max=(\d+):overlap=0:fence=on:merge=(\d+):fallback=cp-exact-(\d+)$/.exec(sig);
|
|
22
|
+
const m = /^c1:enc=cl100k_base:max=(\d+):overlap=0:fence=on:merge=(\d+):fallback=cp-(?:exact|window)-(\d+)$/.exec(sig);
|
|
18
23
|
if (!m)
|
|
19
24
|
return false;
|
|
20
25
|
const max = Number(m[1]);
|
|
@@ -104,33 +109,75 @@ export function buildBlocks(lines) {
|
|
|
104
109
|
}
|
|
105
110
|
return blocks;
|
|
106
111
|
}
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
+
// Oversize split for a block with no structural cut (spec §4.3-6; kg-relation-evidence D3).
|
|
113
|
+
// 6.4.0 scanned every codepoint prefix and re-encoded it (one encode per codepoint, quadratic per
|
|
114
|
+
// chunk; 20k chars 4.6 s, 575k 162 s measured). Now: encode a window once, map the first maxTokens
|
|
115
|
+
// window tokens back to a codepoint count, then VERIFY that candidate by encoding the real substring —
|
|
116
|
+
// the window tokenisation is only an estimate, because BPE re-tokenises a truncated final piece
|
|
117
|
+
// differently (prefix counts are not monotonic: '/sdkX' -> 1,1,2,1,2). Shrink the token count
|
|
118
|
+
// (1, 2, 4, ... fewer) until a candidate verifies; if none does, fall back to the 6.4.0 linear scan for
|
|
119
|
+
// this chunk only, which also keeps its throw. Contract (chunk-invariants, chunker-c): lossless,
|
|
120
|
+
// contiguous codepoint offsets, every chunk's ACTUAL encode length <= maxTokens, deterministic, always
|
|
121
|
+
// advances. Not a longest-fit guarantee — neither was LOOKAHEAD.
|
|
112
122
|
const LOOKAHEAD = 16;
|
|
113
|
-
|
|
123
|
+
const WINDOW_MIN_CP = 64;
|
|
124
|
+
const WINDOW_CP_PER_TOKEN = 8;
|
|
125
|
+
function utf8Bytes(cp) {
|
|
126
|
+
const c = cp.codePointAt(0);
|
|
127
|
+
return c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; // a lone surrogate encodes as U+FFFD = 3
|
|
128
|
+
}
|
|
129
|
+
function linearFit(cps, start, enc, maxTokens) {
|
|
130
|
+
let lastFit = 0;
|
|
131
|
+
let missesSinceFit = 0;
|
|
132
|
+
for (let probe = start + 1; probe <= cps.length && missesSinceFit < LOOKAHEAD; probe++) {
|
|
133
|
+
const t = enc.encode(cps.slice(start, probe).join('')).length;
|
|
134
|
+
if (t <= maxTokens) {
|
|
135
|
+
lastFit = probe - start;
|
|
136
|
+
missesSinceFit = 0;
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
missesSinceFit++;
|
|
140
|
+
}
|
|
141
|
+
return lastFit;
|
|
142
|
+
}
|
|
143
|
+
function windowFit(cps, start, enc, maxTokens) {
|
|
144
|
+
const rest = cps.length - start;
|
|
145
|
+
let w = Math.min(rest, Math.max(WINDOW_MIN_CP, maxTokens * WINDOW_CP_PER_TOKEN));
|
|
146
|
+
let toks = enc.encode(cps.slice(start, start + w).join(''));
|
|
147
|
+
while (toks.length <= maxTokens && w < rest) {
|
|
148
|
+
w = Math.min(rest, w * 2);
|
|
149
|
+
toks = enc.encode(cps.slice(start, start + w).join(''));
|
|
150
|
+
}
|
|
151
|
+
if (toks.length <= maxTokens)
|
|
152
|
+
return rest; // the whole remainder fits
|
|
153
|
+
for (let keep = maxTokens, step = 1; keep >= 1; keep -= step, step *= 2) {
|
|
154
|
+
const bytes = enc.decode(toks.slice(0, keep)).length;
|
|
155
|
+
let used = 0;
|
|
156
|
+
let c = 0;
|
|
157
|
+
while (c < w && used + utf8Bytes(cps[start + c]) <= bytes) {
|
|
158
|
+
used += utf8Bytes(cps[start + c]);
|
|
159
|
+
c++;
|
|
160
|
+
}
|
|
161
|
+
if (c === 0)
|
|
162
|
+
continue;
|
|
163
|
+
if (enc.encode(cps.slice(start, start + c).join('')).length <= maxTokens)
|
|
164
|
+
return c;
|
|
165
|
+
}
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
export function splitOversize(block, enc, maxTokens) {
|
|
114
169
|
const cps = [...block];
|
|
115
170
|
const out = [];
|
|
116
171
|
let start = 0;
|
|
117
172
|
while (start < cps.length) {
|
|
118
|
-
let
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (t <= maxTokens) {
|
|
123
|
-
lastFit = probe - start;
|
|
124
|
-
missesSinceFit = 0;
|
|
125
|
-
}
|
|
126
|
-
else
|
|
127
|
-
missesSinceFit++;
|
|
128
|
-
}
|
|
129
|
-
if (lastFit === 0) {
|
|
173
|
+
let take = windowFit(cps, start, enc, maxTokens);
|
|
174
|
+
if (take === 0)
|
|
175
|
+
take = linearFit(cps, start, enc, maxTokens);
|
|
176
|
+
if (take === 0) {
|
|
130
177
|
throw new Error(`chunker c1: codepoint at offset ${start} exceeds maxTokens=${maxTokens} on its own — cannot honor the token budget`);
|
|
131
178
|
}
|
|
132
|
-
out.push(cps.slice(start, start +
|
|
133
|
-
start +=
|
|
179
|
+
out.push(cps.slice(start, start + take).join(''));
|
|
180
|
+
start += take;
|
|
134
181
|
}
|
|
135
182
|
return out;
|
|
136
183
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export declare const EVIDENCE_FORMAT: "evidence-v1";
|
|
3
|
+
export declare const EVIDENCE_CAPS: Readonly<{
|
|
4
|
+
maxEntries: 8;
|
|
5
|
+
maxQuoteCodepoints: 4000;
|
|
6
|
+
maxBodyCodepoints: 8000;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const FINGERPRINT_TAG = "kg-evidence-fp/1";
|
|
9
|
+
export declare const LEGACY_CMP_TAG = "kg-legacy-cmp/1";
|
|
10
|
+
export declare const EVIDENCE_SEMANTICS = "caller-selected evidence; the engine verified the references exist verbatim; not an independent proof of the relation";
|
|
11
|
+
export type WhyStatus = 'reason-stated' | 'relation-only' | 'unjudged';
|
|
12
|
+
export interface EvidenceSupportInput {
|
|
13
|
+
why_status?: WhyStatus;
|
|
14
|
+
valid_time?: string;
|
|
15
|
+
scope_note?: string;
|
|
16
|
+
}
|
|
17
|
+
export type EvidenceEntryInput = {
|
|
18
|
+
kind: 'document';
|
|
19
|
+
chunk_id: string;
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
quote_sha256: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'observation';
|
|
25
|
+
observation_id: string;
|
|
26
|
+
revision_no: number;
|
|
27
|
+
content_sha256: string;
|
|
28
|
+
};
|
|
29
|
+
export interface EvidenceItemInput {
|
|
30
|
+
relationship_id: string;
|
|
31
|
+
evidence: EvidenceEntryInput[];
|
|
32
|
+
support?: EvidenceSupportInput;
|
|
33
|
+
}
|
|
34
|
+
export interface SourceTuple {
|
|
35
|
+
source_kind: string;
|
|
36
|
+
source_ref: string;
|
|
37
|
+
source_hash: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface DocumentRefMeta {
|
|
40
|
+
kind: 'document';
|
|
41
|
+
document_id: string;
|
|
42
|
+
chunk_id: string;
|
|
43
|
+
start: number;
|
|
44
|
+
end: number;
|
|
45
|
+
chunk_text_sha256: string;
|
|
46
|
+
quote_sha256: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ObservationRefMeta {
|
|
49
|
+
kind: 'observation';
|
|
50
|
+
observation_id: string;
|
|
51
|
+
entity_id: string;
|
|
52
|
+
revision_no: number;
|
|
53
|
+
content_sha256: string;
|
|
54
|
+
status_at_generation: 'active';
|
|
55
|
+
sources: SourceTuple[];
|
|
56
|
+
source_summary: 'recorded' | 'unrecorded' | 'import-only';
|
|
57
|
+
}
|
|
58
|
+
export type EvidenceRefMeta = DocumentRefMeta | ObservationRefMeta;
|
|
59
|
+
export interface EvidenceChunkMetadataV1 {
|
|
60
|
+
kg_format: typeof EVIDENCE_FORMAT;
|
|
61
|
+
relationship_id: string;
|
|
62
|
+
source_entity_id: string;
|
|
63
|
+
target_entity_id: string;
|
|
64
|
+
source_entity: string;
|
|
65
|
+
target_entity: string;
|
|
66
|
+
relation_type: string;
|
|
67
|
+
direction: 'source->target';
|
|
68
|
+
confidence: number;
|
|
69
|
+
evidence_refs: EvidenceRefMeta[];
|
|
70
|
+
support: {
|
|
71
|
+
why_status: WhyStatus;
|
|
72
|
+
valid_time: string | null;
|
|
73
|
+
scope_note: string | null;
|
|
74
|
+
};
|
|
75
|
+
generated_ref_status: 'refs-resolved';
|
|
76
|
+
input_fingerprint: string;
|
|
77
|
+
fingerprint_profile_id: number;
|
|
78
|
+
body_codepoints: number;
|
|
79
|
+
embedded_truncation: 'unchecked';
|
|
80
|
+
evidence_semantics: string;
|
|
81
|
+
}
|
|
82
|
+
export interface FingerprintInput {
|
|
83
|
+
relationship_id: string;
|
|
84
|
+
source_entity_id: string;
|
|
85
|
+
target_entity_id: string;
|
|
86
|
+
relation_type: string;
|
|
87
|
+
source_name: string;
|
|
88
|
+
target_name: string;
|
|
89
|
+
kg_format: typeof EVIDENCE_FORMAT;
|
|
90
|
+
refs: EvidenceRefMeta[];
|
|
91
|
+
profile_id: number;
|
|
92
|
+
}
|
|
93
|
+
export interface PreparedEvidenceChunk {
|
|
94
|
+
chunk_id: string;
|
|
95
|
+
relationship_id: string;
|
|
96
|
+
text: string;
|
|
97
|
+
body_codepoints: number;
|
|
98
|
+
metadata: EvidenceChunkMetadataV1;
|
|
99
|
+
}
|
|
100
|
+
export interface KgChunkRow {
|
|
101
|
+
rowid: number;
|
|
102
|
+
chunk_id: string;
|
|
103
|
+
chunk_type: string | null;
|
|
104
|
+
entity_id: string | null;
|
|
105
|
+
relationship_id: string | null;
|
|
106
|
+
text: string | null;
|
|
107
|
+
metadata: string | null;
|
|
108
|
+
}
|
|
109
|
+
export interface KgEmbedCapture {
|
|
110
|
+
rowid: number;
|
|
111
|
+
chunk_id: string;
|
|
112
|
+
text_sha256: string;
|
|
113
|
+
cmp: string | null;
|
|
114
|
+
profile_id: number;
|
|
115
|
+
}
|
|
116
|
+
export declare function cpLength(s: string): number;
|
|
117
|
+
export declare function cpSlice(s: string, start: number, end: number): string;
|
|
118
|
+
export declare function sha256Utf8(s: string): string;
|
|
119
|
+
export declare function compareSourceTuples(a: SourceTuple, b: SourceTuple): number;
|
|
120
|
+
export declare function fingerprintSerialization(f: FingerprintInput): string;
|
|
121
|
+
export declare function computeInputFingerprint(f: FingerprintInput): string;
|
|
122
|
+
export declare function fingerprintFromMetadata(m: EvidenceChunkMetadataV1, profileId: number): string;
|
|
123
|
+
export declare function buildEvidenceBody(sourceName: string, relationType: string, targetName: string, quotes: string[]): string;
|
|
124
|
+
export declare function evidenceChunkId(relationshipId: string): string;
|
|
125
|
+
export declare function parseEvidenceMetadata(raw: string | null): EvidenceChunkMetadataV1 | null;
|
|
126
|
+
export declare function kgComparisonValue(row: KgChunkRow): string | null;
|
|
127
|
+
export declare function captureKgEmbedInput(row: KgChunkRow, profileId: number): KgEmbedCapture;
|
|
128
|
+
export declare class EvidenceInputError extends Error {
|
|
129
|
+
readonly code: string;
|
|
130
|
+
readonly itemIndex: number;
|
|
131
|
+
constructor(code: string, itemIndex: number, detail: string);
|
|
132
|
+
}
|
|
133
|
+
export declare class EvidenceCommitConflictError extends Error {
|
|
134
|
+
readonly itemIndex: number;
|
|
135
|
+
readonly reason: string;
|
|
136
|
+
constructor(itemIndex: number, reason: string);
|
|
137
|
+
}
|
|
138
|
+
export declare function readSourceTuples(db: Database.Database, observationId: string): SourceTuple[];
|
|
139
|
+
export declare function resolveEvidenceItem(db: Database.Database, item: EvidenceItemInput, itemIndex: number, profileId: number): PreparedEvidenceChunk;
|
|
140
|
+
export type EvidenceStatus = 'claim_only' | 'refs_available' | 'refs_unavailable';
|
|
141
|
+
export interface EvidenceRefStatus {
|
|
142
|
+
index: number;
|
|
143
|
+
kind: string;
|
|
144
|
+
status: 'available' | 'missing' | 'mismatch';
|
|
145
|
+
ref: unknown;
|
|
146
|
+
text: string | null;
|
|
147
|
+
}
|
|
148
|
+
export declare function resolveEvidenceStatus(db: Database.Database, meta: Record<string, unknown> | null): {
|
|
149
|
+
status: EvidenceStatus;
|
|
150
|
+
refs: EvidenceRefStatus[];
|
|
151
|
+
};
|
|
152
|
+
export declare function isSourceTuple(v: unknown): v is SourceTuple;
|
|
153
|
+
export declare function isWellFormedRef(v: unknown): boolean;
|
|
154
|
+
export declare function collectEvidenceRowidsCitingObservations(db: Database.Database, observationIds: string[]): number[];
|
|
155
|
+
export declare function collectEvidenceRowidsCitingDocument(db: Database.Database, documentId: string): number[];
|
|
156
|
+
export declare function collectEvidenceRowidsCitingOwnerEntity(db: Database.Database, entityId: string): number[];
|