@oh-my-pi/pi-mnemopi 16.1.13 → 16.1.14
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
2
|
import { sha256Hex16 } from "../../util/ids";
|
|
3
|
+
import { cjkFtsTerms, containsSpacelessCjk, ftsQueryTerms, recallTokens } from "../../util/regex";
|
|
3
4
|
import type { BeamMemoryState, Metadata } from "./types";
|
|
4
5
|
export type Vector = number[];
|
|
5
6
|
export type HybridWeights = readonly [vecWeight: number, ftsWeight: number, importanceWeight: number];
|
|
@@ -29,16 +30,8 @@ export declare function parseQueryTime(queryTime?: string | Date | null): Date;
|
|
|
29
30
|
export declare function parseTimestampFast(ts: string | null | undefined, beam?: Pick<BeamMemoryState, "caches"> | null): Date | null;
|
|
30
31
|
export declare function recencyDecay(timestamp: string | null | undefined, halflifeHours?: number, now?: Date): number;
|
|
31
32
|
export declare function temporalBoost(memoryTimestamp: string | null | undefined, queryTime: Date | string, halflifeHours?: number, beam?: Pick<BeamMemoryState, "caches"> | null): number;
|
|
32
|
-
export declare function recallTokens(text: string): string[];
|
|
33
|
-
export declare function expandedQueryTokens(tokens: readonly string[]): string[];
|
|
34
|
-
export declare function minimumRecallRelevance(queryTokens: readonly string[]): number;
|
|
35
|
-
export declare function factMatchTokens(text: string): Set<string>;
|
|
36
|
-
export declare function containsSpacelessCjk(text: string): boolean;
|
|
37
|
-
export declare function hasCjk(text: string): boolean;
|
|
38
|
-
export declare function cjkFtsTerms(text: string): string[];
|
|
39
33
|
export declare function lexicalRelevance(queryTokens: readonly string[], content: string, queryLower?: string): number;
|
|
40
34
|
export declare function strictFactMatches(query: string, factText: string): boolean;
|
|
41
|
-
export declare function ftsQueryTerms(query: string): string[];
|
|
42
35
|
export declare function buildFtsQuery(query: string): string;
|
|
43
36
|
export declare function cjkLikeSearch(db: Database, query: string, k?: number, working?: boolean): Array<FtsRankResult | WorkingFtsRankResult>;
|
|
44
37
|
export declare function ftsSearch(db: Database, query: string, k?: number): FtsRankResult[];
|
|
@@ -56,7 +49,7 @@ export declare function metadataJson(input: unknown): string;
|
|
|
56
49
|
export declare function detectLanguage(text: string): string;
|
|
57
50
|
export declare function memoryRowMetadata(row: unknown): Metadata;
|
|
58
51
|
export { cosineSimilarity, hammingDistance, informationTheoreticScore, maximallyInformativeBinarization, quantizeInt8, } from "../binary-vectors";
|
|
59
|
-
export { sha256Hex16 };
|
|
52
|
+
export { cjkFtsTerms, containsSpacelessCjk, ftsQueryTerms, recallTokens, sha256Hex16 };
|
|
60
53
|
/** Identifies one freshly stored memory whose embedding still needs to be derived. */
|
|
61
54
|
export interface EmbedItem {
|
|
62
55
|
readonly memoryId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const FACT_MATCH_STOPWORDS: Set<string>;
|
|
2
2
|
export declare const RECALL_SYNONYMS: Readonly<Record<string, readonly string[]>>;
|
|
3
|
+
export declare function isCjkChar(ch: string): boolean;
|
|
3
4
|
export declare function hasCjk(text: string): boolean;
|
|
4
5
|
export declare const containsSpacelessCjk: typeof hasCjk;
|
|
5
6
|
export declare function recallTokens(text: string): string[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-mnemopi",
|
|
4
|
-
"version": "16.1.
|
|
4
|
+
"version": "16.1.14",
|
|
5
5
|
"description": "Local SQLite memory engine for Oh My Pi agents",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"fmt": "biome format --write ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "16.1.
|
|
43
|
-
"@oh-my-pi/pi-catalog": "16.1.
|
|
44
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
42
|
+
"@oh-my-pi/pi-ai": "16.1.14",
|
|
43
|
+
"@oh-my-pi/pi-catalog": "16.1.14",
|
|
44
|
+
"@oh-my-pi/pi-utils": "16.1.14",
|
|
45
45
|
"lru-cache": "11.5.1"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
package/src/core/beam/helpers.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
2
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { generateId as generateTimedId, sha256Hex16, stableMemoryId } from "../../util/ids";
|
|
4
|
+
import {
|
|
5
|
+
cjkFtsTerms,
|
|
6
|
+
containsSpacelessCjk,
|
|
7
|
+
FACT_MATCH_STOPWORDS,
|
|
8
|
+
factMatchTokens,
|
|
9
|
+
ftsQueryTerms,
|
|
10
|
+
hasCjk,
|
|
11
|
+
isCjkChar,
|
|
12
|
+
RECALL_SYNONYMS,
|
|
13
|
+
recallTokens,
|
|
14
|
+
} from "../../util/regex";
|
|
4
15
|
import { currentEmbeddingModel, embed } from "../embeddings";
|
|
5
16
|
import { getMnemopiRuntimeOptions, mnemopiDebugEnabled, withMnemopiRuntimeOptions } from "../runtime-options";
|
|
6
17
|
import { buildExactVectorIndex, searchExactVectorIndex } from "../vector-index";
|
|
@@ -35,76 +46,8 @@ const DEFAULT_WEIGHTS: HybridWeights = [0.5, 0.3, 0.2];
|
|
|
35
46
|
const TS_CACHE_MAX = 2000;
|
|
36
47
|
const moduleTimestampCache = new Map<string, Date>();
|
|
37
48
|
|
|
38
|
-
const FACT_MATCH_STOPWORDS = new Set([
|
|
39
|
-
"a",
|
|
40
|
-
"an",
|
|
41
|
-
"and",
|
|
42
|
-
"are",
|
|
43
|
-
"as",
|
|
44
|
-
"at",
|
|
45
|
-
"be",
|
|
46
|
-
"by",
|
|
47
|
-
"can",
|
|
48
|
-
"could",
|
|
49
|
-
"did",
|
|
50
|
-
"do",
|
|
51
|
-
"does",
|
|
52
|
-
"for",
|
|
53
|
-
"from",
|
|
54
|
-
"had",
|
|
55
|
-
"has",
|
|
56
|
-
"have",
|
|
57
|
-
"how",
|
|
58
|
-
"i",
|
|
59
|
-
"in",
|
|
60
|
-
"is",
|
|
61
|
-
"it",
|
|
62
|
-
"its",
|
|
63
|
-
"me",
|
|
64
|
-
"my",
|
|
65
|
-
"of",
|
|
66
|
-
"on",
|
|
67
|
-
"or",
|
|
68
|
-
"our",
|
|
69
|
-
"related",
|
|
70
|
-
"should",
|
|
71
|
-
"that",
|
|
72
|
-
"the",
|
|
73
|
-
"their",
|
|
74
|
-
"there",
|
|
75
|
-
"this",
|
|
76
|
-
"to",
|
|
77
|
-
"totally",
|
|
78
|
-
"unrelated",
|
|
79
|
-
"use",
|
|
80
|
-
"uses",
|
|
81
|
-
"was",
|
|
82
|
-
"we",
|
|
83
|
-
"what",
|
|
84
|
-
"when",
|
|
85
|
-
"where",
|
|
86
|
-
"which",
|
|
87
|
-
"who",
|
|
88
|
-
"why",
|
|
89
|
-
"with",
|
|
90
|
-
"you",
|
|
91
|
-
"your",
|
|
92
|
-
]);
|
|
93
|
-
|
|
94
|
-
const RECALL_SYNONYMS: Readonly<Record<string, readonly string[]>> = {
|
|
95
|
-
branding: ["brand", "positioning", "identity", "wording"],
|
|
96
|
-
preference: ["prefer", "prefers", "want", "wants", "reject", "rejects", "avoid", "grounded"],
|
|
97
|
-
professional: ["software", "builder"],
|
|
98
|
-
url: ["link", "profile"],
|
|
99
|
-
current: ["now", "live", "latest"],
|
|
100
|
-
feeling: ["feel", "feels"],
|
|
101
|
-
imposter: ["self-doubt", "doubt", "insecure"],
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const RECALL_TOKEN_RE = /[a-z0-9][a-z0-9_.:/+-]*/g;
|
|
105
49
|
const SPLIT_TOKEN_RE = /[_:/.-]+/g;
|
|
106
50
|
const WORD_RE = /[\p{L}\p{N}_]+/gu;
|
|
107
|
-
|
|
108
51
|
function envNumber(name: string, fallback: number): number {
|
|
109
52
|
const raw = process.env[name];
|
|
110
53
|
if (raw === undefined || raw.trim() === "") return fallback;
|
|
@@ -123,12 +66,6 @@ function asFiniteNonNegative(value: number): number {
|
|
|
123
66
|
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
124
67
|
}
|
|
125
68
|
|
|
126
|
-
function isCjkChar(ch: string): boolean {
|
|
127
|
-
return (
|
|
128
|
-
(ch >= "\u4e00" && ch <= "\u9fff") || (ch >= "\u3040" && ch <= "\u30ff") || (ch >= "\uac00" && ch <= "\ud7af")
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
69
|
function tableExists(db: Database, table: string): boolean {
|
|
133
70
|
try {
|
|
134
71
|
return (
|
|
@@ -250,73 +187,6 @@ export function temporalBoost(
|
|
|
250
187
|
return Math.exp(-hoursDelta / halflife);
|
|
251
188
|
}
|
|
252
189
|
|
|
253
|
-
export function recallTokens(text: string): string[] {
|
|
254
|
-
const out: string[] = [];
|
|
255
|
-
for (const match of text.toLowerCase().matchAll(RECALL_TOKEN_RE)) {
|
|
256
|
-
const token = match[0] ?? "";
|
|
257
|
-
if (token.length >= 3 && !FACT_MATCH_STOPWORDS.has(token) && !/^\d+$/.test(token)) out.push(token);
|
|
258
|
-
}
|
|
259
|
-
return out;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function expandedQueryTokens(tokens: readonly string[]): string[] {
|
|
263
|
-
const expanded: string[] = [];
|
|
264
|
-
const seen = new Set<string>();
|
|
265
|
-
for (const token of tokens) {
|
|
266
|
-
const synonyms = RECALL_SYNONYMS[token] ?? [];
|
|
267
|
-
for (const candidate of [token, ...synonyms]) {
|
|
268
|
-
if (!seen.has(candidate)) {
|
|
269
|
-
seen.add(candidate);
|
|
270
|
-
expanded.push(candidate);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return expanded;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function minimumRecallRelevance(queryTokens: readonly string[]): number {
|
|
278
|
-
if (queryTokens.length >= 4) return 0.3;
|
|
279
|
-
if (queryTokens.length === 3) return 0.5;
|
|
280
|
-
return 0.15;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export function factMatchTokens(text: string): Set<string> {
|
|
284
|
-
return new Set(recallTokens(text));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function containsSpacelessCjk(text: string): boolean {
|
|
288
|
-
return hasCjk(text);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function hasCjk(text: string): boolean {
|
|
292
|
-
for (const ch of text) if (isCjkChar(ch)) return true;
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function cjkFtsTerms(text: string): string[] {
|
|
297
|
-
const chars = Array.from(text).filter(isCjkChar);
|
|
298
|
-
if (chars.length === 0) return [];
|
|
299
|
-
const terms: string[] = [];
|
|
300
|
-
const seen = new Set<string>();
|
|
301
|
-
for (const ch of chars) {
|
|
302
|
-
if (!seen.has(ch)) {
|
|
303
|
-
seen.add(ch);
|
|
304
|
-
terms.push(ch);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
for (let i = 0; i < chars.length - 1; i += 1) {
|
|
308
|
-
const left = chars[i];
|
|
309
|
-
const right = chars[i + 1];
|
|
310
|
-
if (left === undefined || right === undefined) continue;
|
|
311
|
-
const bigram = left + right;
|
|
312
|
-
if (!seen.has(bigram)) {
|
|
313
|
-
seen.add(bigram);
|
|
314
|
-
terms.push(`"${bigram}"`);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return terms;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
190
|
export function lexicalRelevance(queryTokens: readonly string[], content: string, queryLower = ""): number {
|
|
321
191
|
const contentLower = content.toLowerCase();
|
|
322
192
|
const queryCjk = new Set(Array.from(queryLower).filter(isCjkChar));
|
|
@@ -379,15 +249,6 @@ export function strictFactMatches(query: string, factText: string): boolean {
|
|
|
379
249
|
return token.length >= 5;
|
|
380
250
|
}
|
|
381
251
|
|
|
382
|
-
export function ftsQueryTerms(query: string): string[] {
|
|
383
|
-
const terms: string[] = [];
|
|
384
|
-
for (const term of expandedQueryTokens(recallTokens(query))) {
|
|
385
|
-
const escaped = term.replaceAll('"', '""').trim();
|
|
386
|
-
if (escaped) terms.push(`"${escaped}"`);
|
|
387
|
-
}
|
|
388
|
-
return terms;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
252
|
export function buildFtsQuery(query: string): string {
|
|
392
253
|
return ftsQueryTerms(query).join(" OR ");
|
|
393
254
|
}
|
|
@@ -908,7 +769,7 @@ export {
|
|
|
908
769
|
maximallyInformativeBinarization,
|
|
909
770
|
quantizeInt8,
|
|
910
771
|
} from "../binary-vectors";
|
|
911
|
-
export { sha256Hex16 };
|
|
772
|
+
export { cjkFtsTerms, containsSpacelessCjk, ftsQueryTerms, recallTokens, sha256Hex16 };
|
|
912
773
|
|
|
913
774
|
/** Identifies one freshly stored memory whose embedding still needs to be derived. */
|
|
914
775
|
export interface EmbedItem {
|
package/src/util/regex.ts
CHANGED
|
@@ -67,6 +67,12 @@ export const RECALL_SYNONYMS: Readonly<Record<string, readonly string[]>> = {
|
|
|
67
67
|
imposter: ["self-doubt", "doubt", "insecure"],
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
export function isCjkChar(ch: string): boolean {
|
|
71
|
+
return (
|
|
72
|
+
(ch >= "\u4e00" && ch <= "\u9fff") || (ch >= "\u3040" && ch <= "\u30ff") || (ch >= "\uac00" && ch <= "\ud7af")
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
export function hasCjk(text: string): boolean {
|
|
71
77
|
return CJK_RE.test(text);
|
|
72
78
|
}
|
|
@@ -119,7 +125,7 @@ export function cjkFtsTerms(text: string): string[] {
|
|
|
119
125
|
const chars: string[] = [];
|
|
120
126
|
for (let i = 0; i < text.length; i++) {
|
|
121
127
|
const ch = text.charAt(i);
|
|
122
|
-
if (
|
|
128
|
+
if (isCjkChar(ch)) chars.push(ch);
|
|
123
129
|
}
|
|
124
130
|
if (chars.length === 0) return [];
|
|
125
131
|
const terms: string[] = [];
|
|
@@ -157,9 +163,3 @@ function isAsciiDigits(value: string): boolean {
|
|
|
157
163
|
}
|
|
158
164
|
return value.length > 0;
|
|
159
165
|
}
|
|
160
|
-
|
|
161
|
-
function isCjkCodeUnit(code: number): boolean {
|
|
162
|
-
return (
|
|
163
|
-
(code >= 0x4e00 && code <= 0x9fff) || (code >= 0x3040 && code <= 0x30ff) || (code >= 0xac00 && code <= 0xd7af)
|
|
164
|
-
);
|
|
165
|
-
}
|