@oh-my-pi/pi-mnemopi 18.12.6 → 18.13.0
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.13.0] - 2026-10-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed polyphonic recall's graph voice finding no memories for non-English names (e.g. `Hà Nội`) or names of three or more words, and Vietnamese words like `tôi` being stored as the participant `i` ([#16723](https://github.com/can1357/oh-my-pi/issues/16723))
|
|
10
|
+
|
|
5
11
|
## [18.12.4] - 2026-10-20
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
@@ -55,6 +55,15 @@ export interface EpisodicGraphOptions {
|
|
|
55
55
|
readonly db?: Database;
|
|
56
56
|
readonly dbPath?: DatabasePath;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Capitalized proper nouns of at most two words, in any cased script (Latin, Greek,
|
|
60
|
+
* Cyrillic, Vietnamese, …), deduplicated case-insensitively. Gist participants are
|
|
61
|
+
* stored with this extractor and the polyphonic graph voice looks gists up with it, so
|
|
62
|
+
* a query entity matches a stored participant exactly (`findGistsByParticipant`).
|
|
63
|
+
* Unicode property escapes and `\p{L}`-based lookaround boundaries replace `[A-Z][a-z]+`
|
|
64
|
+
* and ASCII `\b`, which matched no non-English name (issues #7918, #16723).
|
|
65
|
+
*/
|
|
66
|
+
export declare function extractProperNouns(text: string): string[];
|
|
58
67
|
export declare class EpisodicGraph {
|
|
59
68
|
readonly db: Database;
|
|
60
69
|
readonly dbPath: DatabasePath;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-mnemopi",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.13.0",
|
|
5
5
|
"description": "Local SQLite memory engine for omp agents",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@oh-my-pi/pi-ai": "18.
|
|
46
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
47
|
-
"@oh-my-pi/pi-natives": "18.
|
|
48
|
-
"@oh-my-pi/pi-utils": "18.
|
|
45
|
+
"@oh-my-pi/pi-ai": "18.13.0",
|
|
46
|
+
"@oh-my-pi/pi-catalog": "18.13.0",
|
|
47
|
+
"@oh-my-pi/pi-natives": "18.13.0",
|
|
48
|
+
"@oh-my-pi/pi-utils": "18.13.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"fastembed": "3.0.0",
|
|
@@ -134,6 +134,23 @@ function unique(values: Iterable<string>, limit = Number.MAX_SAFE_INTEGER): stri
|
|
|
134
134
|
return out;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Capitalized proper nouns of at most two words, in any cased script (Latin, Greek,
|
|
139
|
+
* Cyrillic, Vietnamese, …), deduplicated case-insensitively. Gist participants are
|
|
140
|
+
* stored with this extractor and the polyphonic graph voice looks gists up with it, so
|
|
141
|
+
* a query entity matches a stored participant exactly (`findGistsByParticipant`).
|
|
142
|
+
* Unicode property escapes and `\p{L}`-based lookaround boundaries replace `[A-Z][a-z]+`
|
|
143
|
+
* and ASCII `\b`, which matched no non-English name (issues #7918, #16723).
|
|
144
|
+
*/
|
|
145
|
+
export function extractProperNouns(text: string): string[] {
|
|
146
|
+
return unique(
|
|
147
|
+
Array.from(
|
|
148
|
+
text.matchAll(/(?<![\p{L}\p{N}_])(\p{Lu}\p{Ll}+(?:\s+\p{Lu}\p{Ll}+)?)(?![\p{L}\p{N}_])/gu),
|
|
149
|
+
match => match[1] ?? "",
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
137
154
|
function parseJsonStringArray(value: string | null): string[] {
|
|
138
155
|
if (value === null || value === "") return [];
|
|
139
156
|
try {
|
|
@@ -604,19 +621,13 @@ export class EpisodicGraph {
|
|
|
604
621
|
}
|
|
605
622
|
|
|
606
623
|
private extractParticipants(content: string): string[] {
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
// memory produced zero participants (issue #7918). Unicode property escapes with
|
|
610
|
-
// the `u` flag, and `\p{L}`-based lookaround boundaries in place of `\b`, fix that.
|
|
611
|
-
const names = Array.from(
|
|
612
|
-
content.matchAll(/(?<![\p{L}\p{N}_])(\p{Lu}\p{Ll}+(?:\s+\p{Lu}\p{Ll}+)?)(?![\p{L}\p{N}_])/gu),
|
|
613
|
-
match => match[1] ?? "",
|
|
614
|
-
);
|
|
624
|
+
// `\p{L}` boundaries, not ASCII `\b`: with `i`, `\b` matched the trailing `i` of
|
|
625
|
+
// words like Vietnamese `tôi` as the pronoun `I` (issue #16723).
|
|
615
626
|
const pronouns = Array.from(
|
|
616
|
-
content.matchAll(
|
|
627
|
+
content.matchAll(/(?<![\p{L}\p{N}_])(I|you|we|they|he|she|it|me|us|them|him|her)(?![\p{L}\p{N}_])/giu),
|
|
617
628
|
match => match[1] ?? "",
|
|
618
629
|
);
|
|
619
|
-
return unique([...
|
|
630
|
+
return unique([...extractProperNouns(content), ...pronouns], 5);
|
|
620
631
|
}
|
|
621
632
|
|
|
622
633
|
private extractTemporalScope(content: string): string | null {
|
|
@@ -4,7 +4,7 @@ import { type Env, polyphonicRecallEnabled } from "../config";
|
|
|
4
4
|
import { closeQuietly, type DatabasePath, openDatabase } from "../db";
|
|
5
5
|
import { backfillConsolidatedFacts, ensureVeracityConsolidator } from "./beam/consolidate";
|
|
6
6
|
import type { BeamMemoryState, JsonValue, Metadata, RecallResult, RecallTierLabel } from "./beam/types";
|
|
7
|
-
import { EpisodicGraph } from "./episodic-graph";
|
|
7
|
+
import { EpisodicGraph, extractProperNouns } from "./episodic-graph";
|
|
8
8
|
import { STOP_WORDS } from "./synonyms";
|
|
9
9
|
import { VeracityConsolidator } from "./veracity-consolidation";
|
|
10
10
|
|
|
@@ -214,16 +214,6 @@ function cosineAgainstUnit(unit: Float32Array, raw: unknown): number | null {
|
|
|
214
214
|
return dot / Math.sqrt(normSq);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
function extractEntities(text: string): string[] {
|
|
218
|
-
const seen = new Set<string>();
|
|
219
|
-
const matches = text.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b/g);
|
|
220
|
-
for (const match of matches) {
|
|
221
|
-
const entity = match[0];
|
|
222
|
-
if (entity.length > 0) seen.add(entity);
|
|
223
|
-
}
|
|
224
|
-
return [...seen];
|
|
225
|
-
}
|
|
226
|
-
|
|
227
217
|
function queryTokens(query: string): string[] {
|
|
228
218
|
const tokens: string[] = [];
|
|
229
219
|
for (const match of query.toLowerCase().matchAll(/[\p{L}\p{N}_-]+/gu)) tokens.push(match[0]);
|
|
@@ -420,7 +410,7 @@ export class PolyphonicRecallEngine {
|
|
|
420
410
|
const existing = seeds.get(hit.memoryId);
|
|
421
411
|
if (existing === undefined || hit.score > existing.score) seeds.set(hit.memoryId, hit);
|
|
422
412
|
};
|
|
423
|
-
for (const entity of
|
|
413
|
+
for (const entity of extractProperNouns(query)) {
|
|
424
414
|
for (const gist of this.graph.findGistsByParticipant(entity)) {
|
|
425
415
|
const memoryId = gist.id.startsWith("gist_") ? gist.id.slice(5) : gist.id;
|
|
426
416
|
addSeed({ memoryId, score: 0.6, voice: "graph", metadata: { entity, gist: gist.text } });
|