@mnemoai/core 1.1.0 → 1.1.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/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.d.ts.map +1 -0
- package/{index.ts → dist/index.js} +526 -1333
- package/dist/index.js.map +7 -0
- package/dist/src/access-tracker.d.ts +97 -0
- package/dist/src/access-tracker.d.ts.map +1 -0
- package/dist/src/access-tracker.js +184 -0
- package/dist/src/access-tracker.js.map +7 -0
- package/dist/src/adapters/chroma.d.ts +31 -0
- package/dist/src/adapters/chroma.d.ts.map +1 -0
- package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
- package/dist/src/adapters/chroma.js.map +7 -0
- package/dist/src/adapters/lancedb.d.ts +29 -0
- package/dist/src/adapters/lancedb.d.ts.map +1 -0
- package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
- package/dist/src/adapters/lancedb.js.map +7 -0
- package/dist/src/adapters/pgvector.d.ts +33 -0
- package/dist/src/adapters/pgvector.d.ts.map +1 -0
- package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
- package/dist/src/adapters/pgvector.js.map +7 -0
- package/dist/src/adapters/qdrant.d.ts +34 -0
- package/dist/src/adapters/qdrant.d.ts.map +1 -0
- package/dist/src/adapters/qdrant.js +132 -0
- package/dist/src/adapters/qdrant.js.map +7 -0
- package/dist/src/adaptive-retrieval.d.ts +14 -0
- package/dist/src/adaptive-retrieval.d.ts.map +1 -0
- package/dist/src/adaptive-retrieval.js +52 -0
- package/dist/src/adaptive-retrieval.js.map +7 -0
- package/dist/src/audit-log.d.ts +56 -0
- package/dist/src/audit-log.d.ts.map +1 -0
- package/dist/src/audit-log.js +139 -0
- package/dist/src/audit-log.js.map +7 -0
- package/dist/src/chunker.d.ts +45 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +157 -0
- package/dist/src/chunker.js.map +7 -0
- package/dist/src/config.d.ts +70 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +142 -0
- package/dist/src/config.js.map +7 -0
- package/dist/src/decay-engine.d.ts +73 -0
- package/dist/src/decay-engine.d.ts.map +1 -0
- package/dist/src/decay-engine.js +119 -0
- package/dist/src/decay-engine.js.map +7 -0
- package/dist/src/embedder.d.ts +94 -0
- package/dist/src/embedder.d.ts.map +1 -0
- package/{src/embedder.ts → dist/src/embedder.js} +119 -317
- package/dist/src/embedder.js.map +7 -0
- package/dist/src/extraction-prompts.d.ts +12 -0
- package/dist/src/extraction-prompts.d.ts.map +1 -0
- package/dist/src/extraction-prompts.js +311 -0
- package/dist/src/extraction-prompts.js.map +7 -0
- package/dist/src/license.d.ts +29 -0
- package/dist/src/license.d.ts.map +1 -0
- package/{src/license.ts → dist/src/license.js} +42 -113
- package/dist/src/license.js.map +7 -0
- package/dist/src/llm-client.d.ts +23 -0
- package/dist/src/llm-client.d.ts.map +1 -0
- package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
- package/dist/src/llm-client.js.map +7 -0
- package/dist/src/logger.d.ts +33 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +35 -0
- package/dist/src/logger.js.map +7 -0
- package/dist/src/mcp-server.d.ts +16 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
- package/dist/src/mcp-server.js.map +7 -0
- package/dist/src/memory-categories.d.ts +40 -0
- package/dist/src/memory-categories.d.ts.map +1 -0
- package/dist/src/memory-categories.js +33 -0
- package/dist/src/memory-categories.js.map +7 -0
- package/dist/src/memory-upgrader.d.ts +71 -0
- package/dist/src/memory-upgrader.d.ts.map +1 -0
- package/dist/src/memory-upgrader.js +238 -0
- package/dist/src/memory-upgrader.js.map +7 -0
- package/dist/src/migrate.d.ts +47 -0
- package/dist/src/migrate.d.ts.map +1 -0
- package/{src/migrate.ts → dist/src/migrate.js} +57 -165
- package/dist/src/migrate.js.map +7 -0
- package/dist/src/mnemo.d.ts +67 -0
- package/dist/src/mnemo.d.ts.map +1 -0
- package/dist/src/mnemo.js +66 -0
- package/dist/src/mnemo.js.map +7 -0
- package/dist/src/noise-filter.d.ts +23 -0
- package/dist/src/noise-filter.d.ts.map +1 -0
- package/dist/src/noise-filter.js +62 -0
- package/dist/src/noise-filter.js.map +7 -0
- package/dist/src/noise-prototypes.d.ts +40 -0
- package/dist/src/noise-prototypes.d.ts.map +1 -0
- package/dist/src/noise-prototypes.js +116 -0
- package/dist/src/noise-prototypes.js.map +7 -0
- package/dist/src/observability.d.ts +16 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +53 -0
- package/dist/src/observability.js.map +7 -0
- package/dist/src/query-tracker.d.ts +27 -0
- package/dist/src/query-tracker.d.ts.map +1 -0
- package/dist/src/query-tracker.js +32 -0
- package/dist/src/query-tracker.js.map +7 -0
- package/dist/src/reflection-event-store.d.ts +44 -0
- package/dist/src/reflection-event-store.d.ts.map +1 -0
- package/dist/src/reflection-event-store.js +50 -0
- package/dist/src/reflection-event-store.js.map +7 -0
- package/dist/src/reflection-item-store.d.ts +58 -0
- package/dist/src/reflection-item-store.d.ts.map +1 -0
- package/dist/src/reflection-item-store.js +69 -0
- package/dist/src/reflection-item-store.js.map +7 -0
- package/dist/src/reflection-mapped-metadata.d.ts +47 -0
- package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
- package/dist/src/reflection-mapped-metadata.js +40 -0
- package/dist/src/reflection-mapped-metadata.js.map +7 -0
- package/dist/src/reflection-metadata.d.ts +11 -0
- package/dist/src/reflection-metadata.d.ts.map +1 -0
- package/dist/src/reflection-metadata.js +24 -0
- package/dist/src/reflection-metadata.js.map +7 -0
- package/dist/src/reflection-ranking.d.ts +13 -0
- package/dist/src/reflection-ranking.d.ts.map +1 -0
- package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
- package/dist/src/reflection-ranking.js.map +7 -0
- package/dist/src/reflection-retry.d.ts +30 -0
- package/dist/src/reflection-retry.d.ts.map +1 -0
- package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
- package/dist/src/reflection-retry.js.map +7 -0
- package/dist/src/reflection-slices.d.ts +42 -0
- package/dist/src/reflection-slices.d.ts.map +1 -0
- package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
- package/dist/src/reflection-slices.js.map +7 -0
- package/dist/src/reflection-store.d.ts +85 -0
- package/dist/src/reflection-store.d.ts.map +1 -0
- package/dist/src/reflection-store.js +407 -0
- package/dist/src/reflection-store.js.map +7 -0
- package/dist/src/resonance-state.d.ts +19 -0
- package/dist/src/resonance-state.d.ts.map +1 -0
- package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
- package/dist/src/resonance-state.js.map +7 -0
- package/dist/src/retriever.d.ts +228 -0
- package/dist/src/retriever.d.ts.map +1 -0
- package/dist/src/retriever.js +1006 -0
- package/dist/src/retriever.js.map +7 -0
- package/dist/src/scopes.d.ts +58 -0
- package/dist/src/scopes.d.ts.map +1 -0
- package/dist/src/scopes.js +252 -0
- package/dist/src/scopes.js.map +7 -0
- package/dist/src/self-improvement-files.d.ts +20 -0
- package/dist/src/self-improvement-files.d.ts.map +1 -0
- package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
- package/dist/src/self-improvement-files.js.map +7 -0
- package/dist/src/semantic-gate.d.ts +24 -0
- package/dist/src/semantic-gate.d.ts.map +1 -0
- package/dist/src/semantic-gate.js +86 -0
- package/dist/src/semantic-gate.js.map +7 -0
- package/dist/src/session-recovery.d.ts +9 -0
- package/dist/src/session-recovery.d.ts.map +1 -0
- package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
- package/dist/src/session-recovery.js.map +7 -0
- package/dist/src/smart-extractor.d.ts +107 -0
- package/dist/src/smart-extractor.d.ts.map +1 -0
- package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
- package/dist/src/smart-extractor.js.map +7 -0
- package/dist/src/smart-metadata.d.ts +103 -0
- package/dist/src/smart-metadata.d.ts.map +1 -0
- package/dist/src/smart-metadata.js +361 -0
- package/dist/src/smart-metadata.js.map +7 -0
- package/dist/src/storage-adapter.d.ts +102 -0
- package/dist/src/storage-adapter.d.ts.map +1 -0
- package/dist/src/storage-adapter.js +22 -0
- package/dist/src/storage-adapter.js.map +7 -0
- package/dist/src/store.d.ts +108 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +939 -0
- package/dist/src/store.js.map +7 -0
- package/dist/src/tier-manager.d.ts +57 -0
- package/dist/src/tier-manager.d.ts.map +1 -0
- package/dist/src/tier-manager.js +80 -0
- package/dist/src/tier-manager.js.map +7 -0
- package/dist/src/tools.d.ts +43 -0
- package/dist/src/tools.d.ts.map +1 -0
- package/dist/src/tools.js +1075 -0
- package/dist/src/tools.js.map +7 -0
- package/dist/src/wal-recovery.d.ts +30 -0
- package/dist/src/wal-recovery.d.ts.map +1 -0
- package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
- package/dist/src/wal-recovery.js.map +7 -0
- package/package.json +21 -2
- package/openclaw.plugin.json +0 -815
- package/src/access-tracker.ts +0 -341
- package/src/adapters/README.md +0 -78
- package/src/adapters/qdrant.ts +0 -191
- package/src/adaptive-retrieval.ts +0 -90
- package/src/audit-log.ts +0 -238
- package/src/chunker.ts +0 -254
- package/src/config.ts +0 -271
- package/src/decay-engine.ts +0 -238
- package/src/extraction-prompts.ts +0 -339
- package/src/memory-categories.ts +0 -71
- package/src/memory-upgrader.ts +0 -388
- package/src/mnemo.ts +0 -142
- package/src/noise-filter.ts +0 -97
- package/src/noise-prototypes.ts +0 -164
- package/src/observability.ts +0 -81
- package/src/query-tracker.ts +0 -57
- package/src/reflection-event-store.ts +0 -98
- package/src/reflection-item-store.ts +0 -112
- package/src/reflection-mapped-metadata.ts +0 -84
- package/src/reflection-metadata.ts +0 -23
- package/src/reflection-store.ts +0 -602
- package/src/retriever.ts +0 -1510
- package/src/scopes.ts +0 -375
- package/src/semantic-gate.ts +0 -121
- package/src/smart-metadata.ts +0 -561
- package/src/storage-adapter.ts +0 -153
- package/src/store.ts +0 -1330
- package/src/tier-manager.ts +0 -189
- package/src/tools.ts +0 -1292
- package/test/core.test.mjs +0 -301
package/src/smart-metadata.ts
DELETED
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
import type { MemoryCategory, MemoryTier } from "./memory-categories.js";
|
|
3
|
-
import type { DecayableMemory } from "./decay-engine.js";
|
|
4
|
-
|
|
5
|
-
type LegacyStoreCategory =
|
|
6
|
-
| "preference"
|
|
7
|
-
| "fact"
|
|
8
|
-
| "decision"
|
|
9
|
-
| "entity"
|
|
10
|
-
| "other"
|
|
11
|
-
| "reflection";
|
|
12
|
-
|
|
13
|
-
type EntryLike = {
|
|
14
|
-
text?: string;
|
|
15
|
-
category?: LegacyStoreCategory;
|
|
16
|
-
importance?: number;
|
|
17
|
-
timestamp?: number;
|
|
18
|
-
metadata?: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export interface SmartMemoryMetadata {
|
|
22
|
-
l0_abstract: string;
|
|
23
|
-
l1_overview: string;
|
|
24
|
-
l2_content: string;
|
|
25
|
-
memory_category: MemoryCategory;
|
|
26
|
-
tier: MemoryTier;
|
|
27
|
-
access_count: number;
|
|
28
|
-
confidence: number;
|
|
29
|
-
last_accessed_at: number;
|
|
30
|
-
source_session?: string;
|
|
31
|
-
/** Emotional salience score (0-1). Higher = more emotionally significant = decays slower.
|
|
32
|
-
* Computed at store time via heuristic rules (zero LLM cost). */
|
|
33
|
-
emotional_salience: number;
|
|
34
|
-
[key: string]: unknown;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface LifecycleMemory {
|
|
38
|
-
id: string;
|
|
39
|
-
importance: number;
|
|
40
|
-
confidence: number;
|
|
41
|
-
tier: MemoryTier;
|
|
42
|
-
accessCount: number;
|
|
43
|
-
createdAt: number;
|
|
44
|
-
lastAccessedAt: number;
|
|
45
|
-
emotionalSalience: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function clamp01(value: unknown, fallback: number): number {
|
|
49
|
-
const n = typeof value === "number" ? value : Number(value);
|
|
50
|
-
if (!Number.isFinite(n)) return fallback;
|
|
51
|
-
return Math.min(1, Math.max(0, n));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function clampCount(value: unknown, fallback = 0): number {
|
|
55
|
-
const n = typeof value === "number" ? value : Number(value);
|
|
56
|
-
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
57
|
-
return Math.floor(n);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ============================================================================
|
|
61
|
-
// Emotional Salience — heuristic scoring (zero LLM cost)
|
|
62
|
-
// ============================================================================
|
|
63
|
-
|
|
64
|
-
/** High-salience signal patterns (decisions, strong emotions, firsts, money, people) */
|
|
65
|
-
const SALIENCE_BOOSTERS: Array<{ pattern: RegExp; boost: number }> = [
|
|
66
|
-
// Decisions and commitments
|
|
67
|
-
{ pattern: /\b(决定|决策|confirmed|decided|commit|approved|批了|拍板|定了)\b/i, boost: 0.3 },
|
|
68
|
-
// Strong emotions
|
|
69
|
-
{ pattern: /\b(震惊|惊喜|愤怒|失望|兴奋|amazing|shocked|frustrated|excited|worried|担心)\b/i, boost: 0.25 },
|
|
70
|
-
// First-time events
|
|
71
|
-
{ pattern: /\b(第一次|首次|first time|first ever|从未|never before)\b/i, boost: 0.25 },
|
|
72
|
-
// Financial significance
|
|
73
|
-
{ pattern: /\b(\d+万|\d+亿|\$[\d,.]+[MBK]|估值|valuation|投资|持仓)\b/i, boost: 0.2 },
|
|
74
|
-
// People and relationships (user-configurable entity names)
|
|
75
|
-
{ pattern: /\b(colleague|partner|mentor|friend|manager|teammate)\b/i, boost: 0.15 },
|
|
76
|
-
// Lessons learned / mistakes
|
|
77
|
-
{ pattern: /\b(教训|踩坑|pitfall|lesson|mistake|bug|故障|挂了|崩了)\b/i, boost: 0.2 },
|
|
78
|
-
// Preferences and identity
|
|
79
|
-
{ pattern: /\b(喜欢|讨厌|偏好|prefer|hate|love|always|never)\b/i, boost: 0.15 },
|
|
80
|
-
// Exclamation / emphasis (emotional weight)
|
|
81
|
-
{ pattern: /[!!]{2,}|‼️|⚠️|🔴|💀/, boost: 0.1 },
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
/** Low-salience patterns (routine, technical noise) */
|
|
85
|
-
const SALIENCE_DAMPENERS: Array<{ pattern: RegExp; dampen: number }> = [
|
|
86
|
-
{ pattern: /\b(heartbeat|HEARTBEAT_OK)\b/i, dampen: 0.2 },
|
|
87
|
-
{ pattern: /\b(cron|restart|gateway|status)\b/i, dampen: 0.1 },
|
|
88
|
-
{ pattern: /\b(debug|stack trace|npm|node_modules|\.tsx?|\.jsx?)\b/i, dampen: 0.1 },
|
|
89
|
-
{ pattern: /^\[?(Updated|Added|Removed|Fixed|Set)\]?\s.{0,30}$/i, dampen: 0.15 }, // short auto-generated entries only
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Compute emotional salience from text + category.
|
|
94
|
-
* Returns 0-1. Higher = more emotionally charged / personally significant.
|
|
95
|
-
* Pure heuristic, no LLM call.
|
|
96
|
-
*/
|
|
97
|
-
export function computeEmotionalSalience(
|
|
98
|
-
text: string,
|
|
99
|
-
category?: string,
|
|
100
|
-
importance?: number,
|
|
101
|
-
): number {
|
|
102
|
-
let score = 0.35; // baseline — neutral memory
|
|
103
|
-
|
|
104
|
-
// Category boost
|
|
105
|
-
if (category === "decision") score += 0.15;
|
|
106
|
-
if (category === "preference") score += 0.1;
|
|
107
|
-
if (category === "reflection") score += 0.1;
|
|
108
|
-
|
|
109
|
-
// Importance as a weak signal
|
|
110
|
-
if (typeof importance === "number" && importance > 0.8) score += 0.1;
|
|
111
|
-
if (typeof importance === "number" && importance > 0.9) score += 0.05;
|
|
112
|
-
|
|
113
|
-
// Pattern matching
|
|
114
|
-
for (const { pattern, boost } of SALIENCE_BOOSTERS) {
|
|
115
|
-
if (pattern.test(text)) score += boost;
|
|
116
|
-
}
|
|
117
|
-
for (const { pattern, dampen } of SALIENCE_DAMPENERS) {
|
|
118
|
-
if (pattern.test(text)) score -= dampen;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return Math.min(1, Math.max(0, score));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ============================================================================
|
|
125
|
-
// Emotion Calibration — rule-based post-processing for LLM valence
|
|
126
|
-
// ============================================================================
|
|
127
|
-
|
|
128
|
-
/** Strong emotion signal words (positive or negative) */
|
|
129
|
-
const STRONG_EMOTION_PATTERNS = [
|
|
130
|
-
/太好了|太棒了|amazing|incredible|wonderful|fantastic|excellent/i,
|
|
131
|
-
/terrible|horrible|awful|disgusting|devastating/i,
|
|
132
|
-
/fuck|shit|damn|靠|卧槽|我去|妈的/i,
|
|
133
|
-
/哈哈哈|lol|lmao|rofl|😂|🤣/i,
|
|
134
|
-
/[!!]{3,}/,
|
|
135
|
-
/heartbroken|ecstatic|furious|thrilled|terrified/i,
|
|
136
|
-
/崩溃|暴怒|狂喜|绝望|震惊|兴奋死了/i,
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
/** Factual / data-heavy text indicators */
|
|
140
|
-
const FACTUAL_PATTERNS = [
|
|
141
|
-
/估值|revenue|valuation|profit|loss|margin/i,
|
|
142
|
-
/[$¥€£]/,
|
|
143
|
-
/%/,
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Calibrate LLM-returned emotion valence with rule-based post-processing.
|
|
148
|
-
*
|
|
149
|
-
* Fixes two common LLM failure modes:
|
|
150
|
-
* 1. Strong emotional text scored as neutral (0.4-0.6) → push toward extremes
|
|
151
|
-
* 2. Pure factual/data text scored as emotional → compress toward neutral
|
|
152
|
-
*
|
|
153
|
-
* @param text - The memory text
|
|
154
|
-
* @param rawValence - LLM-returned valence (0-1, 0.5 = neutral)
|
|
155
|
-
* @returns Calibrated valence (0-1)
|
|
156
|
-
*/
|
|
157
|
-
export function calibrateEmotion(text: string, rawValence: number): number {
|
|
158
|
-
if (!Number.isFinite(rawValence)) return 0.5;
|
|
159
|
-
|
|
160
|
-
// Check for strong emotion signals
|
|
161
|
-
const hasStrongEmotion = STRONG_EMOTION_PATTERNS.some(p => p.test(text));
|
|
162
|
-
|
|
163
|
-
// Check if text is factual/data-heavy
|
|
164
|
-
const digitChars = (text.match(/\d/g) || []).length;
|
|
165
|
-
const digitRatio = text.length > 0 ? digitChars / text.length : 0;
|
|
166
|
-
const hasFactualSignals = digitRatio > 0.3 || FACTUAL_PATTERNS.some(p => p.test(text));
|
|
167
|
-
|
|
168
|
-
let calibrated = rawValence;
|
|
169
|
-
|
|
170
|
-
// Fix 1: Strong emotion + neutral valence → push to 0.3 or 0.7
|
|
171
|
-
if (hasStrongEmotion && rawValence >= 0.4 && rawValence <= 0.6) {
|
|
172
|
-
// Determine direction: below 0.5 → negative, above → positive
|
|
173
|
-
calibrated = rawValence <= 0.5 ? 0.3 : 0.7;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Fix 2: Factual text → compress to 0.45-0.55
|
|
177
|
-
if (hasFactualSignals && !hasStrongEmotion) {
|
|
178
|
-
if (calibrated < 0.45) calibrated = 0.45;
|
|
179
|
-
if (calibrated > 0.55) calibrated = 0.55;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return Math.min(1, Math.max(0, calibrated));
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function normalizeTier(value: unknown): MemoryTier {
|
|
186
|
-
switch (value) {
|
|
187
|
-
case "core":
|
|
188
|
-
case "working":
|
|
189
|
-
case "peripheral":
|
|
190
|
-
return value;
|
|
191
|
-
default:
|
|
192
|
-
return "working";
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export function reverseMapLegacyCategory(
|
|
197
|
-
oldCategory: LegacyStoreCategory | undefined,
|
|
198
|
-
text = "",
|
|
199
|
-
): MemoryCategory {
|
|
200
|
-
switch (oldCategory) {
|
|
201
|
-
case "preference":
|
|
202
|
-
return "preferences";
|
|
203
|
-
case "entity":
|
|
204
|
-
return "entities";
|
|
205
|
-
case "decision":
|
|
206
|
-
return "events";
|
|
207
|
-
case "other":
|
|
208
|
-
return "patterns";
|
|
209
|
-
case "fact":
|
|
210
|
-
if (
|
|
211
|
-
/\b(my |i am |i'm |name is |叫我|我的|我是)\b/i.test(text) &&
|
|
212
|
-
text.length < 200
|
|
213
|
-
) {
|
|
214
|
-
return "profile";
|
|
215
|
-
}
|
|
216
|
-
return "cases";
|
|
217
|
-
default:
|
|
218
|
-
return "patterns";
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function defaultOverview(text: string): string {
|
|
223
|
-
return `- ${text}`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function normalizeText(value: unknown, fallback: string): string {
|
|
227
|
-
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export function parseSmartMetadata(
|
|
231
|
-
rawMetadata: string | undefined,
|
|
232
|
-
entry: EntryLike = {},
|
|
233
|
-
): SmartMemoryMetadata {
|
|
234
|
-
let parsed: Record<string, unknown> = {};
|
|
235
|
-
if (rawMetadata) {
|
|
236
|
-
try {
|
|
237
|
-
const obj = JSON.parse(rawMetadata);
|
|
238
|
-
if (obj && typeof obj === "object") {
|
|
239
|
-
parsed = obj as Record<string, unknown>;
|
|
240
|
-
}
|
|
241
|
-
} catch {
|
|
242
|
-
parsed = {};
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const text = entry.text ?? "";
|
|
247
|
-
const timestamp =
|
|
248
|
-
typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp)
|
|
249
|
-
? entry.timestamp
|
|
250
|
-
: Date.now();
|
|
251
|
-
|
|
252
|
-
const memoryCategory = reverseMapLegacyCategory(entry.category, text);
|
|
253
|
-
const l0 = normalizeText(parsed.l0_abstract, text);
|
|
254
|
-
const l2 = normalizeText(parsed.l2_content, text);
|
|
255
|
-
const normalized: SmartMemoryMetadata = {
|
|
256
|
-
...parsed,
|
|
257
|
-
l0_abstract: l0,
|
|
258
|
-
l1_overview: normalizeText(parsed.l1_overview, defaultOverview(l0)),
|
|
259
|
-
l2_content: l2,
|
|
260
|
-
memory_category:
|
|
261
|
-
typeof parsed.memory_category === "string"
|
|
262
|
-
? (parsed.memory_category as MemoryCategory)
|
|
263
|
-
: memoryCategory,
|
|
264
|
-
tier: normalizeTier(parsed.tier),
|
|
265
|
-
access_count: clampCount(parsed.access_count, 0),
|
|
266
|
-
confidence: clamp01(parsed.confidence, 0.7),
|
|
267
|
-
last_accessed_at: clampCount(parsed.last_accessed_at, timestamp),
|
|
268
|
-
source_session:
|
|
269
|
-
typeof parsed.source_session === "string" ? parsed.source_session : undefined,
|
|
270
|
-
emotional_salience: clamp01(
|
|
271
|
-
parsed.emotional_salience,
|
|
272
|
-
computeEmotionalSalience(text, entry.category, entry.importance),
|
|
273
|
-
),
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
return normalized;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export function buildSmartMetadata(
|
|
280
|
-
entry: EntryLike,
|
|
281
|
-
patch: Partial<SmartMemoryMetadata> = {},
|
|
282
|
-
): SmartMemoryMetadata {
|
|
283
|
-
const base = parseSmartMetadata(entry.metadata, entry);
|
|
284
|
-
const text = entry.text ?? "";
|
|
285
|
-
|
|
286
|
-
// Calibrate emotional salience: fix LLM mis-scoring of strong emotions / factual text
|
|
287
|
-
const rawSalience = clamp01(
|
|
288
|
-
patch.emotional_salience ?? base.emotional_salience,
|
|
289
|
-
base.emotional_salience,
|
|
290
|
-
);
|
|
291
|
-
const calibratedSalience = calibrateEmotion(text, rawSalience);
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
...base,
|
|
295
|
-
...patch,
|
|
296
|
-
l0_abstract: normalizeText(patch.l0_abstract, base.l0_abstract),
|
|
297
|
-
l1_overview: normalizeText(patch.l1_overview, base.l1_overview),
|
|
298
|
-
l2_content: normalizeText(patch.l2_content, base.l2_content),
|
|
299
|
-
memory_category:
|
|
300
|
-
typeof patch.memory_category === "string"
|
|
301
|
-
? patch.memory_category
|
|
302
|
-
: base.memory_category,
|
|
303
|
-
tier: normalizeTier(patch.tier ?? base.tier),
|
|
304
|
-
access_count: clampCount(patch.access_count, base.access_count),
|
|
305
|
-
confidence: clamp01(patch.confidence, base.confidence),
|
|
306
|
-
last_accessed_at: clampCount(
|
|
307
|
-
patch.last_accessed_at,
|
|
308
|
-
base.last_accessed_at || entry.timestamp || Date.now(),
|
|
309
|
-
),
|
|
310
|
-
source_session:
|
|
311
|
-
typeof patch.source_session === "string"
|
|
312
|
-
? patch.source_session
|
|
313
|
-
: base.source_session,
|
|
314
|
-
emotional_salience: calibratedSalience,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Metadata array size caps — prevent unbounded JSON growth
|
|
319
|
-
const MAX_SOURCES = 20;
|
|
320
|
-
const MAX_HISTORY = 50;
|
|
321
|
-
const MAX_RELATIONS = 16;
|
|
322
|
-
|
|
323
|
-
export function stringifySmartMetadata(
|
|
324
|
-
metadata: SmartMemoryMetadata | Record<string, unknown>,
|
|
325
|
-
): string {
|
|
326
|
-
const capped = { ...metadata } as Record<string, unknown>;
|
|
327
|
-
|
|
328
|
-
// Cap array fields to prevent metadata bloat
|
|
329
|
-
if (Array.isArray(capped.sources) && capped.sources.length > MAX_SOURCES) {
|
|
330
|
-
capped.sources = capped.sources.slice(-MAX_SOURCES); // keep most recent
|
|
331
|
-
}
|
|
332
|
-
if (Array.isArray(capped.history) && capped.history.length > MAX_HISTORY) {
|
|
333
|
-
capped.history = capped.history.slice(-MAX_HISTORY);
|
|
334
|
-
}
|
|
335
|
-
if (Array.isArray(capped.relations) && capped.relations.length > MAX_RELATIONS) {
|
|
336
|
-
capped.relations = capped.relations.slice(0, MAX_RELATIONS);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return JSON.stringify(capped);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
export function toLifecycleMemory(
|
|
343
|
-
id: string,
|
|
344
|
-
entry: EntryLike,
|
|
345
|
-
): LifecycleMemory {
|
|
346
|
-
const metadata = parseSmartMetadata(entry.metadata, entry);
|
|
347
|
-
const createdAt =
|
|
348
|
-
typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp)
|
|
349
|
-
? entry.timestamp
|
|
350
|
-
: Date.now();
|
|
351
|
-
|
|
352
|
-
return {
|
|
353
|
-
id,
|
|
354
|
-
importance:
|
|
355
|
-
typeof entry.importance === "number" && Number.isFinite(entry.importance)
|
|
356
|
-
? entry.importance
|
|
357
|
-
: 0.7,
|
|
358
|
-
confidence: metadata.confidence,
|
|
359
|
-
tier: metadata.tier,
|
|
360
|
-
accessCount: metadata.access_count,
|
|
361
|
-
createdAt,
|
|
362
|
-
lastAccessedAt: metadata.last_accessed_at || createdAt,
|
|
363
|
-
emotionalSalience: metadata.emotional_salience,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Parse a memory entry into both a DecayableMemory (for the decay engine)
|
|
369
|
-
* and the raw SmartMemoryMetadata (for in-place mutation before write-back).
|
|
370
|
-
*/
|
|
371
|
-
export function getDecayableFromEntry(
|
|
372
|
-
entry: EntryLike & { id?: string },
|
|
373
|
-
): { memory: DecayableMemory; meta: SmartMemoryMetadata } {
|
|
374
|
-
const meta = parseSmartMetadata(entry.metadata, entry);
|
|
375
|
-
const createdAt =
|
|
376
|
-
typeof entry.timestamp === "number" && Number.isFinite(entry.timestamp)
|
|
377
|
-
? entry.timestamp
|
|
378
|
-
: Date.now();
|
|
379
|
-
|
|
380
|
-
const memory: DecayableMemory = {
|
|
381
|
-
id: (entry as { id?: string }).id ?? "",
|
|
382
|
-
importance:
|
|
383
|
-
typeof entry.importance === "number" && Number.isFinite(entry.importance)
|
|
384
|
-
? entry.importance
|
|
385
|
-
: 0.7,
|
|
386
|
-
confidence: meta.confidence,
|
|
387
|
-
tier: meta.tier,
|
|
388
|
-
accessCount: meta.access_count,
|
|
389
|
-
createdAt,
|
|
390
|
-
lastAccessedAt: meta.last_accessed_at || createdAt,
|
|
391
|
-
emotionalSalience: meta.emotional_salience,
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
return { memory, meta };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// ============================================================================
|
|
398
|
-
// Contextual Support — optional extension to SmartMemoryMetadata
|
|
399
|
-
// ============================================================================
|
|
400
|
-
|
|
401
|
-
/** Predefined context vocabulary for support slices */
|
|
402
|
-
export const SUPPORT_CONTEXT_VOCABULARY = [
|
|
403
|
-
"general", "morning", "afternoon", "evening", "night",
|
|
404
|
-
"weekday", "weekend", "work", "leisure",
|
|
405
|
-
"summer", "winter", "travel",
|
|
406
|
-
] as const;
|
|
407
|
-
|
|
408
|
-
export type SupportContext = (typeof SUPPORT_CONTEXT_VOCABULARY)[number] | string;
|
|
409
|
-
|
|
410
|
-
/** Max number of context slices per memory to prevent metadata bloat */
|
|
411
|
-
export const MAX_SUPPORT_SLICES = 8;
|
|
412
|
-
|
|
413
|
-
/** A single context-specific support slice */
|
|
414
|
-
export interface ContextualSupport {
|
|
415
|
-
context: SupportContext;
|
|
416
|
-
confirmations: number;
|
|
417
|
-
contradictions: number;
|
|
418
|
-
strength: number; // confirmations / (confirmations + contradictions)
|
|
419
|
-
last_observed_at: number;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/** V2 support info with per-context slices */
|
|
423
|
-
export interface SupportInfoV2 {
|
|
424
|
-
global_strength: number; // weighted average across all slices
|
|
425
|
-
total_observations: number; // sum of all confirmations + contradictions
|
|
426
|
-
slices: ContextualSupport[];
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Normalize a raw context label to a canonical context.
|
|
431
|
-
* Maps common variants and falls back to "general".
|
|
432
|
-
*/
|
|
433
|
-
export function normalizeContext(raw: string | undefined): SupportContext {
|
|
434
|
-
if (!raw || !raw.trim()) return "general";
|
|
435
|
-
const lower = raw.trim().toLowerCase();
|
|
436
|
-
|
|
437
|
-
// Direct vocabulary match
|
|
438
|
-
if ((SUPPORT_CONTEXT_VOCABULARY as readonly string[]).includes(lower)) {
|
|
439
|
-
return lower as SupportContext;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Common Chinese/English mappings
|
|
443
|
-
const aliases: Record<string, SupportContext> = {
|
|
444
|
-
"早上": "morning", "上午": "morning", "早晨": "morning",
|
|
445
|
-
"下午": "afternoon", "傍晚": "evening", "晚上": "evening",
|
|
446
|
-
"深夜": "night", "夜晚": "night", "凌晨": "night",
|
|
447
|
-
"工作日": "weekday", "平时": "weekday",
|
|
448
|
-
"周末": "weekend", "假日": "weekend", "休息日": "weekend",
|
|
449
|
-
"工作": "work", "上班": "work", "办公": "work",
|
|
450
|
-
"休闲": "leisure", "放松": "leisure", "休息": "leisure",
|
|
451
|
-
"夏天": "summer", "夏季": "summer",
|
|
452
|
-
"冬天": "winter", "冬季": "winter",
|
|
453
|
-
"旅行": "travel", "出差": "travel", "旅游": "travel",
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
return aliases[lower] || lower; // keep as custom context if not mapped
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Parse support_info from metadata JSON. Handles V1 (flat) → V2 (sliced) migration.
|
|
461
|
-
*/
|
|
462
|
-
export function parseSupportInfo(raw: unknown): SupportInfoV2 {
|
|
463
|
-
const defaultV2: SupportInfoV2 = {
|
|
464
|
-
global_strength: 0.5,
|
|
465
|
-
total_observations: 0,
|
|
466
|
-
slices: [],
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
if (!raw || typeof raw !== "object") return defaultV2;
|
|
470
|
-
const obj = raw as Record<string, unknown>;
|
|
471
|
-
|
|
472
|
-
// V2 format: has slices array
|
|
473
|
-
if (Array.isArray(obj.slices)) {
|
|
474
|
-
return {
|
|
475
|
-
global_strength: typeof obj.global_strength === "number" ? obj.global_strength : 0.5,
|
|
476
|
-
total_observations: typeof obj.total_observations === "number" ? obj.total_observations : 0,
|
|
477
|
-
slices: (obj.slices as Record<string, unknown>[]).filter(
|
|
478
|
-
s => s && typeof s.context === "string",
|
|
479
|
-
).map(s => ({
|
|
480
|
-
context: String(s.context),
|
|
481
|
-
confirmations: typeof s.confirmations === "number" && s.confirmations >= 0 ? s.confirmations : 0,
|
|
482
|
-
contradictions: typeof s.contradictions === "number" && s.contradictions >= 0 ? s.contradictions : 0,
|
|
483
|
-
strength: typeof s.strength === "number" && s.strength >= 0 && s.strength <= 1 ? s.strength : 0.5,
|
|
484
|
-
last_observed_at: typeof s.last_observed_at === "number" ? s.last_observed_at : Date.now(),
|
|
485
|
-
})),
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// V1 format: flat { confirmations, contradictions, strength }
|
|
490
|
-
const conf = typeof obj.confirmations === "number" ? obj.confirmations : 0;
|
|
491
|
-
const contra = typeof obj.contradictions === "number" ? obj.contradictions : 0;
|
|
492
|
-
const total = conf + contra;
|
|
493
|
-
if (total === 0) return defaultV2;
|
|
494
|
-
|
|
495
|
-
return {
|
|
496
|
-
global_strength: total > 0 ? conf / total : 0.5,
|
|
497
|
-
total_observations: total,
|
|
498
|
-
slices: [{
|
|
499
|
-
context: "general",
|
|
500
|
-
confirmations: conf,
|
|
501
|
-
contradictions: contra,
|
|
502
|
-
strength: total > 0 ? conf / total : 0.5,
|
|
503
|
-
last_observed_at: Date.now(),
|
|
504
|
-
}],
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Update support stats for a specific context.
|
|
510
|
-
* Returns a new SupportInfoV2 with the updated slice.
|
|
511
|
-
*/
|
|
512
|
-
export function updateSupportStats(
|
|
513
|
-
existing: SupportInfoV2,
|
|
514
|
-
contextLabel: string | undefined,
|
|
515
|
-
event: "support" | "contradict",
|
|
516
|
-
): SupportInfoV2 {
|
|
517
|
-
const ctx = normalizeContext(contextLabel);
|
|
518
|
-
const base = { ...existing, slices: [...existing.slices.map(s => ({ ...s }))] };
|
|
519
|
-
|
|
520
|
-
// Find or create the context slice
|
|
521
|
-
let slice = base.slices.find(s => s.context === ctx);
|
|
522
|
-
if (!slice) {
|
|
523
|
-
slice = { context: ctx, confirmations: 0, contradictions: 0, strength: 0.5, last_observed_at: Date.now() };
|
|
524
|
-
base.slices.push(slice);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Update slice
|
|
528
|
-
if (event === "support") slice.confirmations++;
|
|
529
|
-
else slice.contradictions++;
|
|
530
|
-
const sliceTotal = slice.confirmations + slice.contradictions;
|
|
531
|
-
slice.strength = sliceTotal > 0 ? slice.confirmations / sliceTotal : 0.5;
|
|
532
|
-
slice.last_observed_at = Date.now();
|
|
533
|
-
|
|
534
|
-
// Cap slices (keep most recently observed, but preserve dropped evidence).
|
|
535
|
-
// NOTE: Evidence from slices dropped in *previous* updates is already baked
|
|
536
|
-
// into total_observations/global_strength, so those values may drift slightly
|
|
537
|
-
// over many truncation cycles. This is an accepted trade-off for bounded JSON size.
|
|
538
|
-
let slices = base.slices;
|
|
539
|
-
let droppedConf = 0, droppedContra = 0;
|
|
540
|
-
if (slices.length > MAX_SUPPORT_SLICES) {
|
|
541
|
-
slices = slices
|
|
542
|
-
.sort((a, b) => b.last_observed_at - a.last_observed_at);
|
|
543
|
-
const dropped = slices.slice(MAX_SUPPORT_SLICES);
|
|
544
|
-
for (const d of dropped) {
|
|
545
|
-
droppedConf += d.confirmations;
|
|
546
|
-
droppedContra += d.contradictions;
|
|
547
|
-
}
|
|
548
|
-
slices = slices.slice(0, MAX_SUPPORT_SLICES);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// Recompute global strength including evidence from dropped slices
|
|
552
|
-
let totalConf = droppedConf, totalContra = droppedContra;
|
|
553
|
-
for (const s of slices) {
|
|
554
|
-
totalConf += s.confirmations;
|
|
555
|
-
totalContra += s.contradictions;
|
|
556
|
-
}
|
|
557
|
-
const totalObs = totalConf + totalContra;
|
|
558
|
-
const global_strength = totalObs > 0 ? totalConf / totalObs : 0.5;
|
|
559
|
-
|
|
560
|
-
return { global_strength, total_observations: totalObs, slices };
|
|
561
|
-
}
|
package/src/storage-adapter.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
/**
|
|
3
|
-
* StorageAdapter — Abstract interface for vector storage backends.
|
|
4
|
-
*
|
|
5
|
-
* Mnemo ships with LanceDB as the default adapter.
|
|
6
|
-
* Implement this interface to add support for other backends
|
|
7
|
-
* (Qdrant, Chroma, Milvus, Pinecone, PGVector, etc.)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// ── Types ──
|
|
11
|
-
|
|
12
|
-
export interface MemoryRecord {
|
|
13
|
-
id: string;
|
|
14
|
-
text: string;
|
|
15
|
-
vector: number[];
|
|
16
|
-
timestamp: number;
|
|
17
|
-
scope: string;
|
|
18
|
-
importance: number;
|
|
19
|
-
category: string;
|
|
20
|
-
metadata: string;
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface SearchResult {
|
|
25
|
-
record: MemoryRecord;
|
|
26
|
-
score: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface QueryOptions {
|
|
30
|
-
where?: string;
|
|
31
|
-
select?: string[];
|
|
32
|
-
limit?: number;
|
|
33
|
-
offset?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ── Interface ──
|
|
37
|
-
|
|
38
|
-
export interface StorageAdapter {
|
|
39
|
-
/** Adapter name for logging and diagnostics */
|
|
40
|
-
readonly name: string;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Connect to the storage backend.
|
|
44
|
-
* @param dbPath - path or connection string
|
|
45
|
-
*/
|
|
46
|
-
connect(dbPath: string): Promise<void>;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Ensure the memories table exists with proper schema.
|
|
50
|
-
* Creates the table if it doesn't exist, opens it if it does.
|
|
51
|
-
* @param vectorDimensions - embedding vector dimensions (e.g. 1024)
|
|
52
|
-
*/
|
|
53
|
-
ensureTable(vectorDimensions: number): Promise<void>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Insert one or more records.
|
|
57
|
-
*/
|
|
58
|
-
add(records: MemoryRecord[]): Promise<void>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Update a record by ID. Typically delete + re-insert.
|
|
62
|
-
*/
|
|
63
|
-
update(id: string, record: MemoryRecord): Promise<void>;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Delete records matching a filter expression.
|
|
67
|
-
* @param filter - SQL-like filter string, e.g. "id = 'abc'"
|
|
68
|
-
*/
|
|
69
|
-
delete(filter: string): Promise<void>;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Vector similarity search.
|
|
73
|
-
* @returns results sorted by descending similarity score (0-1)
|
|
74
|
-
*/
|
|
75
|
-
vectorSearch(
|
|
76
|
-
vector: number[],
|
|
77
|
-
limit: number,
|
|
78
|
-
minScore?: number,
|
|
79
|
-
scopeFilter?: string[],
|
|
80
|
-
): Promise<SearchResult[]>;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Full-text (BM25) search.
|
|
84
|
-
* @returns results sorted by descending relevance score
|
|
85
|
-
*/
|
|
86
|
-
fullTextSearch(
|
|
87
|
-
query: string,
|
|
88
|
-
limit: number,
|
|
89
|
-
scopeFilter?: string[],
|
|
90
|
-
): Promise<SearchResult[]>;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* General-purpose query with optional filter, select, limit.
|
|
94
|
-
*/
|
|
95
|
-
query(options: QueryOptions): Promise<MemoryRecord[]>;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Count records matching an optional filter.
|
|
99
|
-
*/
|
|
100
|
-
count(filter?: string): Promise<number>;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Ensure full-text search index exists on the text field.
|
|
104
|
-
*/
|
|
105
|
-
ensureFullTextIndex(): Promise<void>;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check if full-text search is supported and initialized.
|
|
109
|
-
*/
|
|
110
|
-
hasFullTextSearch(): boolean;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Close the connection / cleanup resources.
|
|
114
|
-
*/
|
|
115
|
-
close(): Promise<void>;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ── Factory ──
|
|
119
|
-
|
|
120
|
-
export type AdapterFactory = (config?: Record<string, unknown>) => StorageAdapter;
|
|
121
|
-
|
|
122
|
-
const _registry = new Map<string, AdapterFactory>();
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Register a storage adapter backend.
|
|
126
|
-
* @example registerAdapter("qdrant", (config) => new QdrantAdapter(config))
|
|
127
|
-
*/
|
|
128
|
-
export function registerAdapter(name: string, factory: AdapterFactory): void {
|
|
129
|
-
_registry.set(name, factory);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Create a storage adapter by name.
|
|
134
|
-
* Falls back to LanceDB if name is not registered.
|
|
135
|
-
*/
|
|
136
|
-
export function createAdapter(name: string, config?: Record<string, unknown>): StorageAdapter {
|
|
137
|
-
const factory = _registry.get(name);
|
|
138
|
-
if (!factory) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Storage adapter "${name}" not found. ` +
|
|
141
|
-
`Available: ${[..._registry.keys()].join(", ") || "(none)"}. ` +
|
|
142
|
-
`Did you forget to call registerAdapter()?`
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return factory(config);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* List all registered adapter names.
|
|
150
|
-
*/
|
|
151
|
-
export function listAdapters(): string[] {
|
|
152
|
-
return [..._registry.keys()];
|
|
153
|
-
}
|