@oh-my-pi/pi-mnemopi 18.2.4 → 18.2.6

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.2.5] - 2026-09-17
6
+
7
+ ### Fixed
8
+
9
+ - Improved SQLite vector search and import performance by reducing repeated database metadata checks.
10
+
5
11
  ## [18.2.1] - 2026-09-15
6
12
 
7
13
  ### Fixed
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.2.4",
4
+ "version": "18.2.6",
5
5
  "description": "Local SQLite memory engine for Oh My Pi agents",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -39,10 +39,10 @@
39
39
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
40
40
  },
41
41
  "dependencies": {
42
- "@oh-my-pi/pi-ai": "18.2.4",
43
- "@oh-my-pi/pi-catalog": "18.2.4",
44
- "@oh-my-pi/pi-natives": "18.2.4",
45
- "@oh-my-pi/pi-utils": "18.2.4"
42
+ "@oh-my-pi/pi-ai": "18.2.6",
43
+ "@oh-my-pi/pi-catalog": "18.2.6",
44
+ "@oh-my-pi/pi-natives": "18.2.6",
45
+ "@oh-my-pi/pi-utils": "18.2.6"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "fastembed": "2.1.0",
@@ -352,19 +352,29 @@ export function vecAvailable(db: Database): boolean {
352
352
  return tableExists(db, "vec_episodes");
353
353
  }
354
354
 
355
+ // Per-Database memo for the vec table shape: schema is fixed at open, so
356
+ // probing sqlite_master on every insert/search is pure overhead. WeakMap
357
+ // keeps no Database alive.
358
+ const vecTypeMemo = new WeakMap<Database, "float32" | "int8" | "bit">();
359
+
355
360
  export function effectiveVecType(db: Database): "float32" | "int8" | "bit" {
356
- if (!vecAvailable(db)) return "float32";
357
- try {
358
- const row = db.query("SELECT sql FROM sqlite_master WHERE type='table' AND name='vec_episodes'").get() as {
359
- sql?: string;
360
- } | null;
361
- const sql = row?.sql ?? "";
362
- if (sql.includes("int8")) return "int8";
363
- if (sql.includes("bit")) return "bit";
364
- } catch {
365
- return "float32";
361
+ const cached = vecTypeMemo.get(db);
362
+ if (cached !== undefined) return cached;
363
+ let resolved: "float32" | "int8" | "bit" = "float32";
364
+ if (vecAvailable(db)) {
365
+ try {
366
+ const row = db.query("SELECT sql FROM sqlite_master WHERE type='table' AND name='vec_episodes'").get() as {
367
+ sql?: string;
368
+ } | null;
369
+ const sql = row?.sql ?? "";
370
+ if (sql.includes("int8")) resolved = "int8";
371
+ else if (sql.includes("bit")) resolved = "bit";
372
+ } catch {
373
+ resolved = "float32";
374
+ }
366
375
  }
367
- return "float32";
376
+ vecTypeMemo.set(db, resolved);
377
+ return resolved;
368
378
  }
369
379
 
370
380
  export function vecInsert(db: Database, rowid: number, embedding: readonly number[]): void {
@@ -908,13 +908,20 @@ export function importFromDict(beam: BeamMemoryState, data: Record<string, unkno
908
908
  const importedAt = toUtcIso();
909
909
  const oldToNewRowid = new Map<number, number>();
910
910
 
911
+ // Hoisted out of the per-row loops: `db.query` is Bun's cached-statement
912
+ // API (no per-row prepare/finalize churn), and the vec availability probe
913
+ // is loop-invariant (schema is fixed at open).
914
+ const workingExists = db.query("SELECT 1 FROM working_memory WHERE id = ?");
915
+ const episodicExists = db.query("SELECT 1 FROM episodic_memory WHERE id = ?");
916
+ const episodicRowid = db.query("SELECT rowid FROM episodic_memory WHERE id = ?");
917
+ const scratchExists = db.query("SELECT 1 FROM scratchpad WHERE id = ?");
918
+ const vecEpisodesAvailable = vecAvailable(db);
911
919
  transaction(db, () => {
912
920
  for (const raw of Array.isArray(data.working_memory) ? data.working_memory : []) {
913
921
  const item = jsonObject(raw);
914
922
  const id = String(item.id ?? "");
915
923
  if (id.length === 0) continue;
916
- using existsStatement = db.prepare("SELECT 1 FROM working_memory WHERE id = ?");
917
- const exists = existsStatement.get(id) !== null;
924
+ const exists = workingExists.get(id) !== null;
918
925
  if (exists && !force) {
919
926
  stats.working_memory.skipped++;
920
927
  continue;
@@ -968,18 +975,16 @@ export function importFromDict(beam: BeamMemoryState, data: Record<string, unkno
968
975
  const item = jsonObject(raw);
969
976
  const id = String(item.id ?? "");
970
977
  if (id.length === 0) continue;
971
- using existsStatement = db.prepare("SELECT 1 FROM episodic_memory WHERE id = ?");
972
- const exists = existsStatement.get(id) !== null;
978
+ const exists = episodicExists.get(id) !== null;
973
979
  if (exists && !force) {
974
980
  stats.episodic_memory.skipped++;
975
981
  continue;
976
982
  }
977
- using rowidStatement = db.prepare("SELECT rowid FROM episodic_memory WHERE id = ?");
978
983
  if (exists) {
979
- const existingRow = rowidStatement.get(id) as {
984
+ const existingRow = episodicRowid.get(id) as {
980
985
  rowid: number;
981
986
  } | null;
982
- if (existingRow !== null && vecAvailable(db)) {
987
+ if (existingRow !== null && vecEpisodesAvailable) {
983
988
  try {
984
989
  db.run("DELETE FROM vec_episodes WHERE rowid = ?", [existingRow.rowid]);
985
990
  } catch {
@@ -1027,7 +1032,7 @@ export function importFromDict(beam: BeamMemoryState, data: Record<string, unkno
1027
1032
  ],
1028
1033
  );
1029
1034
  const oldRowid = Number(item.rowid);
1030
- const newRow = rowidStatement.get(id) as {
1035
+ const newRow = episodicRowid.get(id) as {
1031
1036
  rowid: number;
1032
1037
  } | null;
1033
1038
  if (Number.isFinite(oldRowid) && newRow !== null) oldToNewRowid.set(oldRowid, newRow.rowid);
@@ -1041,7 +1046,7 @@ export function importFromDict(beam: BeamMemoryState, data: Record<string, unkno
1041
1046
  if (mappedRowid === undefined || embedding === null || embedding.some(v => !Number.isFinite(v))) {
1042
1047
  continue;
1043
1048
  }
1044
- if (!vecAvailable(db)) continue;
1049
+ if (!vecEpisodesAvailable) continue;
1045
1050
  try {
1046
1051
  vecInsert(db, mappedRowid, embedding);
1047
1052
  stats.episodic_memory.embeddings_inserted++;
@@ -1054,8 +1059,7 @@ export function importFromDict(beam: BeamMemoryState, data: Record<string, unkno
1054
1059
  const item = jsonObject(raw);
1055
1060
  const id = String(item.id ?? "");
1056
1061
  if (id.length === 0) continue;
1057
- using existsStatement = db.prepare("SELECT 1 FROM scratchpad WHERE id = ?");
1058
- const exists = existsStatement.get(id) !== null;
1062
+ const exists = scratchExists.get(id) !== null;
1059
1063
  if (exists) {
1060
1064
  db.run("UPDATE scratchpad SET content = ?, session_id = ?, created_at = ?, updated_at = ? WHERE id = ?", [
1061
1065
  sqlBinding(item.content, ""),