@molroo-io/sdk 0.5.2
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/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/cjs/api-client.d.ts +23 -0
- package/dist/cjs/api-client.d.ts.map +1 -0
- package/dist/cjs/api-client.js +55 -0
- package/dist/cjs/defaults/index.d.ts +8 -0
- package/dist/cjs/defaults/index.d.ts.map +1 -0
- package/dist/cjs/defaults/index.js +30 -0
- package/dist/cjs/defaults/persona.json +17 -0
- package/dist/cjs/embedding/cloudflare.d.ts +15 -0
- package/dist/cjs/embedding/cloudflare.d.ts.map +1 -0
- package/dist/cjs/embedding/cloudflare.js +16 -0
- package/dist/cjs/embedding/cohere.d.ts +8 -0
- package/dist/cjs/embedding/cohere.d.ts.map +1 -0
- package/dist/cjs/embedding/cohere.js +31 -0
- package/dist/cjs/embedding/index.d.ts +9 -0
- package/dist/cjs/embedding/index.d.ts.map +1 -0
- package/dist/cjs/embedding/index.js +11 -0
- package/dist/cjs/embedding/local.d.ts +6 -0
- package/dist/cjs/embedding/local.d.ts.map +1 -0
- package/dist/cjs/embedding/local.js +28 -0
- package/dist/cjs/embedding/openai.d.ts +9 -0
- package/dist/cjs/embedding/openai.d.ts.map +1 -0
- package/dist/cjs/embedding/openai.js +26 -0
- package/dist/cjs/errors.d.ts +17 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +21 -0
- package/dist/cjs/events/console.d.ts +25 -0
- package/dist/cjs/events/console.d.ts.map +1 -0
- package/dist/cjs/events/console.js +41 -0
- package/dist/cjs/events/types.d.ts +28 -0
- package/dist/cjs/events/types.d.ts.map +1 -0
- package/dist/cjs/events/types.js +13 -0
- package/dist/cjs/events/webhook.d.ts +30 -0
- package/dist/cjs/events/webhook.d.ts.map +1 -0
- package/dist/cjs/events/webhook.js +79 -0
- package/dist/cjs/generate/persona.d.ts +16 -0
- package/dist/cjs/generate/persona.d.ts.map +1 -0
- package/dist/cjs/generate/persona.js +42 -0
- package/dist/cjs/generate/prompt.d.ts +7 -0
- package/dist/cjs/generate/prompt.d.ts.map +1 -0
- package/dist/cjs/generate/prompt.js +41 -0
- package/dist/cjs/generate/schema.d.ts +32 -0
- package/dist/cjs/generate/schema.d.ts.map +1 -0
- package/dist/cjs/generate/schema.js +54 -0
- package/dist/cjs/generated/index.d.ts +2 -0
- package/dist/cjs/generated/index.d.ts.map +1 -0
- package/dist/cjs/generated/index.js +2 -0
- package/dist/cjs/index.d.ts +66 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +69 -0
- package/dist/cjs/llm/adapter.d.ts +61 -0
- package/dist/cjs/llm/adapter.d.ts.map +1 -0
- package/dist/cjs/llm/adapter.js +2 -0
- package/dist/cjs/llm/resolve.d.ts +28 -0
- package/dist/cjs/llm/resolve.d.ts.map +1 -0
- package/dist/cjs/llm/resolve.js +20 -0
- package/dist/cjs/llm/schema.d.ts +60 -0
- package/dist/cjs/llm/schema.d.ts.map +1 -0
- package/dist/cjs/llm/schema.js +72 -0
- package/dist/cjs/llm/types.d.ts +24 -0
- package/dist/cjs/llm/types.d.ts.map +1 -0
- package/dist/cjs/llm/types.js +2 -0
- package/dist/cjs/llm/vercel-ai/adapter.d.ts +29 -0
- package/dist/cjs/llm/vercel-ai/adapter.d.ts.map +1 -0
- package/dist/cjs/llm/vercel-ai/adapter.js +234 -0
- package/dist/cjs/llm/vercel-ai/config.d.ts +9 -0
- package/dist/cjs/llm/vercel-ai/config.d.ts.map +1 -0
- package/dist/cjs/llm/vercel-ai/config.js +2 -0
- package/dist/cjs/llm/vercel-ai/index.d.ts +9 -0
- package/dist/cjs/llm/vercel-ai/index.d.ts.map +1 -0
- package/dist/cjs/llm/vercel-ai/index.js +13 -0
- package/dist/cjs/memory/cloudflare/index.d.ts +3 -0
- package/dist/cjs/memory/cloudflare/index.d.ts.map +1 -0
- package/dist/cjs/memory/cloudflare/index.js +5 -0
- package/dist/cjs/memory/cloudflare/vectorize.d.ts +62 -0
- package/dist/cjs/memory/cloudflare/vectorize.d.ts.map +1 -0
- package/dist/cjs/memory/cloudflare/vectorize.js +55 -0
- package/dist/cjs/memory/in-memory-semantic.d.ts +16 -0
- package/dist/cjs/memory/in-memory-semantic.d.ts.map +1 -0
- package/dist/cjs/memory/in-memory-semantic.js +57 -0
- package/dist/cjs/memory/in-memory.d.ts +46 -0
- package/dist/cjs/memory/in-memory.d.ts.map +1 -0
- package/dist/cjs/memory/in-memory.js +115 -0
- package/dist/cjs/memory/pinecone/index.d.ts +7 -0
- package/dist/cjs/memory/pinecone/index.d.ts.map +1 -0
- package/dist/cjs/memory/pinecone/index.js +8 -0
- package/dist/cjs/memory/pinecone/memory-adapter.d.ts +62 -0
- package/dist/cjs/memory/pinecone/memory-adapter.d.ts.map +1 -0
- package/dist/cjs/memory/pinecone/memory-adapter.js +220 -0
- package/dist/cjs/memory/pinecone/semantic.d.ts +44 -0
- package/dist/cjs/memory/pinecone/semantic.d.ts.map +1 -0
- package/dist/cjs/memory/pinecone/semantic.js +90 -0
- package/dist/cjs/memory/recall.d.ts +58 -0
- package/dist/cjs/memory/recall.d.ts.map +1 -0
- package/dist/cjs/memory/recall.js +220 -0
- package/dist/cjs/memory/semantic.d.ts +24 -0
- package/dist/cjs/memory/semantic.d.ts.map +1 -0
- package/dist/cjs/memory/semantic.js +2 -0
- package/dist/cjs/memory/sqlite/index.d.ts +3 -0
- package/dist/cjs/memory/sqlite/index.d.ts.map +1 -0
- package/dist/cjs/memory/sqlite/index.js +5 -0
- package/dist/cjs/memory/sqlite/memory-adapter.d.ts +58 -0
- package/dist/cjs/memory/sqlite/memory-adapter.d.ts.map +1 -0
- package/dist/cjs/memory/sqlite/memory-adapter.js +336 -0
- package/dist/cjs/memory/sqlite/schema.d.ts +4 -0
- package/dist/cjs/memory/sqlite/schema.d.ts.map +1 -0
- package/dist/cjs/memory/sqlite/schema.js +91 -0
- package/dist/cjs/memory/supabase/index.d.ts +7 -0
- package/dist/cjs/memory/supabase/index.d.ts.map +1 -0
- package/dist/cjs/memory/supabase/index.js +8 -0
- package/dist/cjs/memory/supabase/memory-adapter.d.ts +67 -0
- package/dist/cjs/memory/supabase/memory-adapter.d.ts.map +1 -0
- package/dist/cjs/memory/supabase/memory-adapter.js +335 -0
- package/dist/cjs/memory/supabase/semantic.d.ts +44 -0
- package/dist/cjs/memory/supabase/semantic.d.ts.map +1 -0
- package/dist/cjs/memory/supabase/semantic.js +72 -0
- package/dist/cjs/memory/types.d.ts +231 -0
- package/dist/cjs/memory/types.d.ts.map +1 -0
- package/dist/cjs/memory/types.js +12 -0
- package/dist/cjs/persona.d.ts +326 -0
- package/dist/cjs/persona.d.ts.map +1 -0
- package/dist/cjs/persona.js +824 -0
- package/dist/cjs/types.d.ts +263 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +15 -0
- package/dist/cjs/world/client.d.ts +36 -0
- package/dist/cjs/world/client.d.ts.map +1 -0
- package/dist/cjs/world/client.js +59 -0
- package/dist/cjs/world/errors.d.ts +9 -0
- package/dist/cjs/world/errors.d.ts.map +1 -0
- package/dist/cjs/world/errors.js +15 -0
- package/dist/cjs/world/index.d.ts +10 -0
- package/dist/cjs/world/index.d.ts.map +1 -0
- package/dist/cjs/world/index.js +16 -0
- package/dist/cjs/world/types.d.ts +101 -0
- package/dist/cjs/world/types.d.ts.map +1 -0
- package/dist/cjs/world/types.js +8 -0
- package/dist/cjs/world/village.d.ts +75 -0
- package/dist/cjs/world/village.d.ts.map +1 -0
- package/dist/cjs/world/village.js +278 -0
- package/dist/cjs/world/world-persona.d.ts +182 -0
- package/dist/cjs/world/world-persona.d.ts.map +1 -0
- package/dist/cjs/world/world-persona.js +192 -0
- package/dist/cjs/world/world.d.ts +41 -0
- package/dist/cjs/world/world.d.ts.map +1 -0
- package/dist/cjs/world/world.js +91 -0
- package/dist/esm/api-client.d.ts +23 -0
- package/dist/esm/api-client.d.ts.map +1 -0
- package/dist/esm/api-client.js +48 -0
- package/dist/esm/defaults/index.d.ts +8 -0
- package/dist/esm/defaults/index.d.ts.map +1 -0
- package/dist/esm/defaults/index.js +23 -0
- package/dist/esm/defaults/persona.json +17 -0
- package/dist/esm/embedding/cloudflare.d.ts +15 -0
- package/dist/esm/embedding/cloudflare.d.ts.map +1 -0
- package/dist/esm/embedding/cloudflare.js +13 -0
- package/dist/esm/embedding/cohere.d.ts +8 -0
- package/dist/esm/embedding/cohere.d.ts.map +1 -0
- package/dist/esm/embedding/cohere.js +28 -0
- package/dist/esm/embedding/index.d.ts +9 -0
- package/dist/esm/embedding/index.d.ts.map +1 -0
- package/dist/esm/embedding/index.js +4 -0
- package/dist/esm/embedding/local.d.ts +6 -0
- package/dist/esm/embedding/local.d.ts.map +1 -0
- package/dist/esm/embedding/local.js +25 -0
- package/dist/esm/embedding/openai.d.ts +9 -0
- package/dist/esm/embedding/openai.d.ts.map +1 -0
- package/dist/esm/embedding/openai.js +23 -0
- package/dist/esm/errors.d.ts +17 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +17 -0
- package/dist/esm/events/console.d.ts +25 -0
- package/dist/esm/events/console.d.ts.map +1 -0
- package/dist/esm/events/console.js +37 -0
- package/dist/esm/events/types.d.ts +28 -0
- package/dist/esm/events/types.d.ts.map +1 -0
- package/dist/esm/events/types.js +12 -0
- package/dist/esm/events/webhook.d.ts +30 -0
- package/dist/esm/events/webhook.d.ts.map +1 -0
- package/dist/esm/events/webhook.js +75 -0
- package/dist/esm/generate/persona.d.ts +16 -0
- package/dist/esm/generate/persona.d.ts.map +1 -0
- package/dist/esm/generate/persona.js +39 -0
- package/dist/esm/generate/prompt.d.ts +7 -0
- package/dist/esm/generate/prompt.d.ts.map +1 -0
- package/dist/esm/generate/prompt.js +38 -0
- package/dist/esm/generate/schema.d.ts +32 -0
- package/dist/esm/generate/schema.d.ts.map +1 -0
- package/dist/esm/generate/schema.js +51 -0
- package/dist/esm/generated/index.d.ts +2 -0
- package/dist/esm/generated/index.d.ts.map +1 -0
- package/dist/esm/generated/index.js +1 -0
- package/dist/esm/index.d.ts +66 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +49 -0
- package/dist/esm/llm/adapter.d.ts +61 -0
- package/dist/esm/llm/adapter.d.ts.map +1 -0
- package/dist/esm/llm/adapter.js +1 -0
- package/dist/esm/llm/resolve.d.ts +28 -0
- package/dist/esm/llm/resolve.d.ts.map +1 -0
- package/dist/esm/llm/resolve.js +17 -0
- package/dist/esm/llm/schema.d.ts +60 -0
- package/dist/esm/llm/schema.d.ts.map +1 -0
- package/dist/esm/llm/schema.js +69 -0
- package/dist/esm/llm/types.d.ts +24 -0
- package/dist/esm/llm/types.d.ts.map +1 -0
- package/dist/esm/llm/types.js +1 -0
- package/dist/esm/llm/vercel-ai/adapter.d.ts +29 -0
- package/dist/esm/llm/vercel-ai/adapter.d.ts.map +1 -0
- package/dist/esm/llm/vercel-ai/adapter.js +196 -0
- package/dist/esm/llm/vercel-ai/config.d.ts +9 -0
- package/dist/esm/llm/vercel-ai/config.d.ts.map +1 -0
- package/dist/esm/llm/vercel-ai/config.js +1 -0
- package/dist/esm/llm/vercel-ai/index.d.ts +9 -0
- package/dist/esm/llm/vercel-ai/index.d.ts.map +1 -0
- package/dist/esm/llm/vercel-ai/index.js +8 -0
- package/dist/esm/memory/cloudflare/index.d.ts +3 -0
- package/dist/esm/memory/cloudflare/index.d.ts.map +1 -0
- package/dist/esm/memory/cloudflare/index.js +1 -0
- package/dist/esm/memory/cloudflare/vectorize.d.ts +62 -0
- package/dist/esm/memory/cloudflare/vectorize.d.ts.map +1 -0
- package/dist/esm/memory/cloudflare/vectorize.js +51 -0
- package/dist/esm/memory/in-memory-semantic.d.ts +16 -0
- package/dist/esm/memory/in-memory-semantic.d.ts.map +1 -0
- package/dist/esm/memory/in-memory-semantic.js +53 -0
- package/dist/esm/memory/in-memory.d.ts +46 -0
- package/dist/esm/memory/in-memory.d.ts.map +1 -0
- package/dist/esm/memory/in-memory.js +111 -0
- package/dist/esm/memory/pinecone/index.d.ts +7 -0
- package/dist/esm/memory/pinecone/index.d.ts.map +1 -0
- package/dist/esm/memory/pinecone/index.js +3 -0
- package/dist/esm/memory/pinecone/memory-adapter.d.ts +62 -0
- package/dist/esm/memory/pinecone/memory-adapter.d.ts.map +1 -0
- package/dist/esm/memory/pinecone/memory-adapter.js +216 -0
- package/dist/esm/memory/pinecone/semantic.d.ts +44 -0
- package/dist/esm/memory/pinecone/semantic.d.ts.map +1 -0
- package/dist/esm/memory/pinecone/semantic.js +86 -0
- package/dist/esm/memory/recall.d.ts +58 -0
- package/dist/esm/memory/recall.d.ts.map +1 -0
- package/dist/esm/memory/recall.js +215 -0
- package/dist/esm/memory/semantic.d.ts +24 -0
- package/dist/esm/memory/semantic.d.ts.map +1 -0
- package/dist/esm/memory/semantic.js +1 -0
- package/dist/esm/memory/sqlite/index.d.ts +3 -0
- package/dist/esm/memory/sqlite/index.d.ts.map +1 -0
- package/dist/esm/memory/sqlite/index.js +1 -0
- package/dist/esm/memory/sqlite/memory-adapter.d.ts +58 -0
- package/dist/esm/memory/sqlite/memory-adapter.d.ts.map +1 -0
- package/dist/esm/memory/sqlite/memory-adapter.js +296 -0
- package/dist/esm/memory/sqlite/schema.d.ts +4 -0
- package/dist/esm/memory/sqlite/schema.d.ts.map +1 -0
- package/dist/esm/memory/sqlite/schema.js +86 -0
- package/dist/esm/memory/supabase/index.d.ts +7 -0
- package/dist/esm/memory/supabase/index.d.ts.map +1 -0
- package/dist/esm/memory/supabase/index.js +3 -0
- package/dist/esm/memory/supabase/memory-adapter.d.ts +67 -0
- package/dist/esm/memory/supabase/memory-adapter.d.ts.map +1 -0
- package/dist/esm/memory/supabase/memory-adapter.js +331 -0
- package/dist/esm/memory/supabase/semantic.d.ts +44 -0
- package/dist/esm/memory/supabase/semantic.d.ts.map +1 -0
- package/dist/esm/memory/supabase/semantic.js +68 -0
- package/dist/esm/memory/types.d.ts +231 -0
- package/dist/esm/memory/types.d.ts.map +1 -0
- package/dist/esm/memory/types.js +9 -0
- package/dist/esm/persona.d.ts +326 -0
- package/dist/esm/persona.d.ts.map +1 -0
- package/dist/esm/persona.js +787 -0
- package/dist/esm/types.d.ts +263 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +11 -0
- package/dist/esm/world/client.d.ts +36 -0
- package/dist/esm/world/client.d.ts.map +1 -0
- package/dist/esm/world/client.js +52 -0
- package/dist/esm/world/errors.d.ts +9 -0
- package/dist/esm/world/errors.d.ts.map +1 -0
- package/dist/esm/world/errors.js +11 -0
- package/dist/esm/world/index.d.ts +10 -0
- package/dist/esm/world/index.d.ts.map +1 -0
- package/dist/esm/world/index.js +8 -0
- package/dist/esm/world/types.d.ts +101 -0
- package/dist/esm/world/types.d.ts.map +1 -0
- package/dist/esm/world/types.js +7 -0
- package/dist/esm/world/village.d.ts +75 -0
- package/dist/esm/world/village.d.ts.map +1 -0
- package/dist/esm/world/village.js +274 -0
- package/dist/esm/world/world-persona.d.ts +182 -0
- package/dist/esm/world/world-persona.d.ts.map +1 -0
- package/dist/esm/world/world-persona.js +188 -0
- package/dist/esm/world/world.d.ts +41 -0
- package/dist/esm/world/world.d.ts.map +1 -0
- package/dist/esm/world/world.js +87 -0
- package/package.json +207 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PineconeMemoryAdapter = void 0;
|
|
4
|
+
const pinecone_1 = require("@pinecone-database/pinecone");
|
|
5
|
+
/**
|
|
6
|
+
* Pinecone-backed MemoryAdapter combining in-memory arrays (RDB) with
|
|
7
|
+
* Pinecone vector search (semanticRecall).
|
|
8
|
+
*
|
|
9
|
+
* RDB operations (saveEpisode, recall, saveReflection, getReflections,
|
|
10
|
+
* getByIds, countEpisodes, updateEpisode, deleteEpisode, expireEpisodes)
|
|
11
|
+
* all operate on in-memory arrays, identical to InMemoryAdapter.
|
|
12
|
+
*
|
|
13
|
+
* Vector search (semanticRecall) delegates to Pinecone after embedding
|
|
14
|
+
* the query text via the provided EmbeddingAdapter.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const memory = new PineconeMemoryAdapter({
|
|
19
|
+
* apiKey: process.env.PINECONE_API_KEY!,
|
|
20
|
+
* indexName: 'molroo-memory',
|
|
21
|
+
* namespace: 'world-123',
|
|
22
|
+
* embedding: openaiEmbedding({ apiKey: '...' }),
|
|
23
|
+
* });
|
|
24
|
+
* await memory.init();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class PineconeMemoryAdapter {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.initialized = false;
|
|
30
|
+
this.episodes = [];
|
|
31
|
+
this.reflections = [];
|
|
32
|
+
this.client = new pinecone_1.Pinecone({ apiKey: options.apiKey });
|
|
33
|
+
this.indexName = options.indexName;
|
|
34
|
+
this.namespace = options.namespace ?? 'default';
|
|
35
|
+
this.embedding = options.embedding;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Initialize the adapter. Must be called before use.
|
|
39
|
+
* Validates embedding dimension against Pinecone index.
|
|
40
|
+
*/
|
|
41
|
+
async init() {
|
|
42
|
+
this.pcIndex = this.client.index(this.indexName);
|
|
43
|
+
const stats = await this.pcIndex.describeIndexStats();
|
|
44
|
+
if (stats.dimension && stats.dimension !== this.embedding.dimension) {
|
|
45
|
+
throw new Error(`Dimension mismatch: Pinecone index expects ${stats.dimension}d, ` +
|
|
46
|
+
`but embedding provider has ${this.embedding.dimension}d`);
|
|
47
|
+
}
|
|
48
|
+
this.initialized = true;
|
|
49
|
+
}
|
|
50
|
+
ensureInitialized() {
|
|
51
|
+
if (!this.initialized) {
|
|
52
|
+
throw new Error('PineconeMemoryAdapter not initialized. Call init() first.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
ns() {
|
|
56
|
+
return this.pcIndex.namespace(this.namespace);
|
|
57
|
+
}
|
|
58
|
+
// ── Required methods (in-memory, identical to InMemoryAdapter) ──
|
|
59
|
+
async saveEpisode(episode) {
|
|
60
|
+
this.ensureInitialized();
|
|
61
|
+
this.episodes.push(episode);
|
|
62
|
+
// Auto-upsert embedding to Pinecone (best-effort)
|
|
63
|
+
const text = episode.context ?? episode.sourceEntity ?? '';
|
|
64
|
+
if (text) {
|
|
65
|
+
try {
|
|
66
|
+
const embedding = await this.embedding.embed(text);
|
|
67
|
+
await this.ns().upsert([{
|
|
68
|
+
id: episode.id,
|
|
69
|
+
values: embedding,
|
|
70
|
+
metadata: {
|
|
71
|
+
type: 'episode',
|
|
72
|
+
sourceEntity: episode.sourceEntity ?? '',
|
|
73
|
+
timestamp: episode.timestamp,
|
|
74
|
+
importance: episode.importance,
|
|
75
|
+
},
|
|
76
|
+
}]);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Best-effort: vector indexing failure doesn't block episode save.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async recall(query) {
|
|
84
|
+
this.ensureInitialized();
|
|
85
|
+
let results = this.episodes;
|
|
86
|
+
if (query.sourceEntity) {
|
|
87
|
+
results = results.filter((e) => e.sourceEntity === query.sourceEntity);
|
|
88
|
+
}
|
|
89
|
+
if (query.context) {
|
|
90
|
+
const ctx = query.context.toLowerCase();
|
|
91
|
+
results = results.filter((e) => e.context && e.context.toLowerCase().includes(ctx));
|
|
92
|
+
}
|
|
93
|
+
if (query.valenceRange) {
|
|
94
|
+
const [min, max] = query.valenceRange;
|
|
95
|
+
results = results.filter((e) => e.emotionSnapshot.V >= min && e.emotionSnapshot.V <= max);
|
|
96
|
+
}
|
|
97
|
+
if (query.minImportance !== undefined) {
|
|
98
|
+
results = results.filter((e) => e.importance >= query.minImportance);
|
|
99
|
+
}
|
|
100
|
+
if (query.timeRange) {
|
|
101
|
+
const [start, end] = query.timeRange;
|
|
102
|
+
results = results.filter((e) => e.timestamp >= start && e.timestamp <= end);
|
|
103
|
+
}
|
|
104
|
+
// Return most recent first
|
|
105
|
+
results = results.slice().sort((a, b) => b.timestamp - a.timestamp);
|
|
106
|
+
if (query.limit && query.limit > 0) {
|
|
107
|
+
results = results.slice(0, query.limit);
|
|
108
|
+
}
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
111
|
+
async saveReflection(reflection) {
|
|
112
|
+
this.ensureInitialized();
|
|
113
|
+
this.reflections.push(reflection);
|
|
114
|
+
}
|
|
115
|
+
async getReflections(sourceEntity) {
|
|
116
|
+
this.ensureInitialized();
|
|
117
|
+
if (sourceEntity) {
|
|
118
|
+
return this.reflections.filter((r) => r.sourceEntity === sourceEntity);
|
|
119
|
+
}
|
|
120
|
+
return [...this.reflections];
|
|
121
|
+
}
|
|
122
|
+
// ── Optional methods ──
|
|
123
|
+
async semanticRecall(query, options) {
|
|
124
|
+
this.ensureInitialized();
|
|
125
|
+
const embedding = await this.embedding.embed(query);
|
|
126
|
+
const topK = options?.topK ?? 5;
|
|
127
|
+
// Build Pinecone filter
|
|
128
|
+
const filter = {};
|
|
129
|
+
if (options?.filter?.sourceEntity) {
|
|
130
|
+
filter.sourceEntity = { $eq: options.filter.sourceEntity };
|
|
131
|
+
}
|
|
132
|
+
if (options?.filter?.minImportance !== undefined) {
|
|
133
|
+
filter.importance = { $gte: options.filter.minImportance };
|
|
134
|
+
}
|
|
135
|
+
// Over-fetch to account for in-memory-only episodes that may not be in Pinecone
|
|
136
|
+
const hasFilter = Object.keys(filter).length > 0;
|
|
137
|
+
const fetchK = hasFilter ? topK * 4 : topK;
|
|
138
|
+
const results = await this.ns().query({
|
|
139
|
+
vector: embedding,
|
|
140
|
+
topK: fetchK,
|
|
141
|
+
filter: hasFilter ? filter : undefined,
|
|
142
|
+
includeMetadata: true,
|
|
143
|
+
});
|
|
144
|
+
// Map Pinecone results to episodes via in-memory lookup
|
|
145
|
+
const matches = results.matches ?? [];
|
|
146
|
+
const idSet = new Set(matches.map((m) => m.id));
|
|
147
|
+
const matched = this.episodes.filter((e) => idSet.has(e.id));
|
|
148
|
+
// Sort by Pinecone score order (highest first), limit to topK
|
|
149
|
+
const scoreMap = new Map(matches.map((m) => [m.id, m.score ?? 0]));
|
|
150
|
+
return matched
|
|
151
|
+
.sort((a, b) => (scoreMap.get(b.id) ?? 0) - (scoreMap.get(a.id) ?? 0))
|
|
152
|
+
.slice(0, topK);
|
|
153
|
+
}
|
|
154
|
+
async getByIds(ids) {
|
|
155
|
+
this.ensureInitialized();
|
|
156
|
+
const idSet = new Set(ids);
|
|
157
|
+
return this.episodes.filter((e) => idSet.has(e.id));
|
|
158
|
+
}
|
|
159
|
+
async countEpisodes(query) {
|
|
160
|
+
this.ensureInitialized();
|
|
161
|
+
let results = this.episodes;
|
|
162
|
+
if (query.sourceEntity) {
|
|
163
|
+
results = results.filter((e) => e.sourceEntity === query.sourceEntity);
|
|
164
|
+
}
|
|
165
|
+
if (query.context) {
|
|
166
|
+
const ctx = query.context.toLowerCase();
|
|
167
|
+
results = results.filter((e) => e.context && e.context.toLowerCase().includes(ctx));
|
|
168
|
+
}
|
|
169
|
+
if (query.timeRange) {
|
|
170
|
+
const [start, end] = query.timeRange;
|
|
171
|
+
results = results.filter((e) => e.timestamp >= start && e.timestamp <= end);
|
|
172
|
+
}
|
|
173
|
+
return results.length;
|
|
174
|
+
}
|
|
175
|
+
async updateEpisode(id, patch) {
|
|
176
|
+
this.ensureInitialized();
|
|
177
|
+
const episode = this.episodes.find((e) => e.id === id);
|
|
178
|
+
if (!episode)
|
|
179
|
+
return;
|
|
180
|
+
if (patch.importance !== undefined)
|
|
181
|
+
episode.importance = patch.importance;
|
|
182
|
+
if (patch.intensity !== undefined)
|
|
183
|
+
episode.intensity = patch.intensity;
|
|
184
|
+
if (patch.emotionSnapshot !== undefined)
|
|
185
|
+
episode.emotionSnapshot = patch.emotionSnapshot;
|
|
186
|
+
}
|
|
187
|
+
async deleteEpisode(id) {
|
|
188
|
+
this.ensureInitialized();
|
|
189
|
+
this.episodes = this.episodes.filter((e) => e.id !== id);
|
|
190
|
+
// Best-effort Pinecone cleanup
|
|
191
|
+
try {
|
|
192
|
+
await this.ns().deleteOne(id);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Non-fatal: Pinecone cleanup failure doesn't affect in-memory state.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async expireEpisodes(before, minImportance) {
|
|
199
|
+
this.ensureInitialized();
|
|
200
|
+
const threshold = minImportance ?? 0;
|
|
201
|
+
const original = this.episodes.length;
|
|
202
|
+
// Collect IDs of episodes to expire for Pinecone cleanup
|
|
203
|
+
const expiredIds = this.episodes
|
|
204
|
+
.filter((e) => e.timestamp < before && e.importance < threshold)
|
|
205
|
+
.map((e) => e.id);
|
|
206
|
+
this.episodes = this.episodes.filter((e) => e.timestamp >= before || e.importance >= threshold);
|
|
207
|
+
const count = original - this.episodes.length;
|
|
208
|
+
// Best-effort Pinecone cleanup
|
|
209
|
+
if (expiredIds.length > 0) {
|
|
210
|
+
try {
|
|
211
|
+
await this.ns().deleteMany({ ids: expiredIds });
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Non-fatal: Pinecone cleanup failure doesn't affect in-memory state.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return count;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.PineconeMemoryAdapter = PineconeMemoryAdapter;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { EmbeddingAdapter } from '../types';
|
|
2
|
+
import type { SemanticMemory, SearchOptions, SearchResult } from '../semantic';
|
|
3
|
+
export interface PineconeSemanticMemoryOptions {
|
|
4
|
+
/** Pinecone API key. */
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/** Pinecone index name. */
|
|
7
|
+
indexName: string;
|
|
8
|
+
/** Pinecone namespace. Default: 'default'. */
|
|
9
|
+
namespace?: string;
|
|
10
|
+
/** Embedding provider (embed function + dimension). */
|
|
11
|
+
embedding: EmbeddingAdapter;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pinecone-backed SemanticMemory.
|
|
15
|
+
*
|
|
16
|
+
* Call `init()` before use to validate embedding dimension against Pinecone index.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const memory = new PineconeSemanticMemory({
|
|
21
|
+
* apiKey: process.env.PINECONE_API_KEY!,
|
|
22
|
+
* indexName: 'my-index',
|
|
23
|
+
* embedding: openaiEmbedding({ apiKey: '...' }),
|
|
24
|
+
* });
|
|
25
|
+
* await memory.init();
|
|
26
|
+
* await memory.store('user likes coffee');
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class PineconeSemanticMemory implements SemanticMemory {
|
|
30
|
+
private client;
|
|
31
|
+
private pcIndex;
|
|
32
|
+
private namespace;
|
|
33
|
+
private indexName;
|
|
34
|
+
private embedding;
|
|
35
|
+
private initialized;
|
|
36
|
+
constructor(options: PineconeSemanticMemoryOptions);
|
|
37
|
+
init(): Promise<void>;
|
|
38
|
+
private ensureInit;
|
|
39
|
+
private ns;
|
|
40
|
+
store(text: string, metadata?: Record<string, unknown>): Promise<string>;
|
|
41
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
42
|
+
delete(ids: string[]): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=semantic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../../../src/memory/pinecone/semantic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,WAAW,6BAA6B;IAC5C,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAOD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,6BAA6B;IAO5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,EAAE;IAIJ,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,OAAO,CAAC,MAAM,CAAC;IAqBZ,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IA4BpB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAM3C"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PineconeSemanticMemory = void 0;
|
|
4
|
+
const pinecone_1 = require("@pinecone-database/pinecone");
|
|
5
|
+
/**
|
|
6
|
+
* Pinecone-backed SemanticMemory.
|
|
7
|
+
*
|
|
8
|
+
* Call `init()` before use to validate embedding dimension against Pinecone index.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const memory = new PineconeSemanticMemory({
|
|
13
|
+
* apiKey: process.env.PINECONE_API_KEY!,
|
|
14
|
+
* indexName: 'my-index',
|
|
15
|
+
* embedding: openaiEmbedding({ apiKey: '...' }),
|
|
16
|
+
* });
|
|
17
|
+
* await memory.init();
|
|
18
|
+
* await memory.store('user likes coffee');
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
class PineconeSemanticMemory {
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.initialized = false;
|
|
24
|
+
this.client = new pinecone_1.Pinecone({ apiKey: options.apiKey });
|
|
25
|
+
this.indexName = options.indexName;
|
|
26
|
+
this.namespace = options.namespace ?? 'default';
|
|
27
|
+
this.embedding = options.embedding;
|
|
28
|
+
}
|
|
29
|
+
async init() {
|
|
30
|
+
this.pcIndex = this.client.index(this.indexName);
|
|
31
|
+
const stats = await this.pcIndex.describeIndexStats();
|
|
32
|
+
if (stats.dimension && stats.dimension !== this.embedding.dimension) {
|
|
33
|
+
throw new Error(`Dimension mismatch: Pinecone index expects ${stats.dimension}d, ` +
|
|
34
|
+
`but embedding provider has ${this.embedding.dimension}d`);
|
|
35
|
+
}
|
|
36
|
+
this.initialized = true;
|
|
37
|
+
}
|
|
38
|
+
ensureInit() {
|
|
39
|
+
if (!this.initialized) {
|
|
40
|
+
throw new Error('PineconeSemanticMemory not initialized. Call init() first.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
ns() {
|
|
44
|
+
return this.pcIndex.namespace(this.namespace);
|
|
45
|
+
}
|
|
46
|
+
async store(text, metadata = {}) {
|
|
47
|
+
this.ensureInit();
|
|
48
|
+
const id = crypto.randomUUID();
|
|
49
|
+
const values = await this.embedding.embed(text);
|
|
50
|
+
const pineconeMetadata = {
|
|
51
|
+
text,
|
|
52
|
+
...Object.fromEntries(Object.entries(metadata).filter(([, v]) => typeof v === 'string' ||
|
|
53
|
+
typeof v === 'number' ||
|
|
54
|
+
typeof v === 'boolean' ||
|
|
55
|
+
Array.isArray(v))),
|
|
56
|
+
};
|
|
57
|
+
await this.ns().upsert([{ id, values, metadata: pineconeMetadata }]);
|
|
58
|
+
return id;
|
|
59
|
+
}
|
|
60
|
+
async search(query, options = {}) {
|
|
61
|
+
this.ensureInit();
|
|
62
|
+
const topK = options.topK ?? 5;
|
|
63
|
+
const threshold = options.scoreThreshold ?? 0;
|
|
64
|
+
const vector = await this.embedding.embed(query);
|
|
65
|
+
const filter = options.filter;
|
|
66
|
+
const hasFilter = filter && Object.keys(filter).length > 0;
|
|
67
|
+
const results = await this.ns().query({
|
|
68
|
+
vector,
|
|
69
|
+
topK,
|
|
70
|
+
filter: hasFilter ? filter : undefined,
|
|
71
|
+
includeMetadata: true,
|
|
72
|
+
});
|
|
73
|
+
const matchList = results.matches ?? [];
|
|
74
|
+
return matchList
|
|
75
|
+
.filter((m) => (m.score ?? 0) >= threshold)
|
|
76
|
+
.map((m) => ({
|
|
77
|
+
id: m.id,
|
|
78
|
+
text: m.metadata?.text ?? '',
|
|
79
|
+
score: m.score ?? 0,
|
|
80
|
+
metadata: m.metadata ?? {},
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
async delete(ids) {
|
|
84
|
+
this.ensureInit();
|
|
85
|
+
if (ids.length > 0) {
|
|
86
|
+
await this.ns().deleteMany({ ids });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.PineconeSemanticMemory = PineconeSemanticMemory;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared memory recall and prompt-building utilities.
|
|
3
|
+
*
|
|
4
|
+
* Used by MolrooPersona to recall episodic/semantic memories and
|
|
5
|
+
* format them for LLM system prompts. Supports both:
|
|
6
|
+
* - Single MemoryAdapter (new, preferred)
|
|
7
|
+
* - Split EpisodeAdapter + SemanticAdapter + EmbeddingAdapter (backward compat)
|
|
8
|
+
*/
|
|
9
|
+
import type { Episode } from '../types';
|
|
10
|
+
import type { EpisodeAdapter, SemanticAdapter, EmbeddingAdapter, MemoryAdapter, Reflection } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Strip leading `[...]` metadata tags from text before embedding.
|
|
13
|
+
* These tags (channel info, scene hints, timestamps) add noise to
|
|
14
|
+
* semantic similarity and should not influence vector search.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stripMetaTags(text: string): string;
|
|
17
|
+
/** Recall tuning parameters. */
|
|
18
|
+
export interface RecallConfig {
|
|
19
|
+
episodicLimit?: number;
|
|
20
|
+
semanticLimit?: number;
|
|
21
|
+
reflectionLimit?: number;
|
|
22
|
+
minImportance?: number;
|
|
23
|
+
/** Filter episodic recall to these episode types. Undefined = all types. */
|
|
24
|
+
types?: string[];
|
|
25
|
+
}
|
|
26
|
+
/** Result of a memory recall operation. */
|
|
27
|
+
export interface RecallResult {
|
|
28
|
+
episodic: Episode[];
|
|
29
|
+
semantic: Episode[];
|
|
30
|
+
reflections: Reflection[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Recall episodic + semantic + reflection memories in parallel.
|
|
34
|
+
*
|
|
35
|
+
* Supports two paths:
|
|
36
|
+
* 1. Single MemoryAdapter (`memoryAdapter` param) — adapter handles everything
|
|
37
|
+
* 2. Split stores (`episodeStore` + `semanticStore` + `embeddingProvider`) — legacy
|
|
38
|
+
*
|
|
39
|
+
* @param message - Current user message (used for semantic embedding).
|
|
40
|
+
* @param opts - Stores, provider, config, and entity filter.
|
|
41
|
+
* @returns Recalled memories grouped by type.
|
|
42
|
+
*/
|
|
43
|
+
export declare function recallMemories(message: string, opts: {
|
|
44
|
+
memoryAdapter?: MemoryAdapter | null;
|
|
45
|
+
episodeStore: EpisodeAdapter | null;
|
|
46
|
+
semanticStore: SemanticAdapter | null;
|
|
47
|
+
embeddingProvider: EmbeddingAdapter | null;
|
|
48
|
+
config?: RecallConfig;
|
|
49
|
+
sourceEntity?: string;
|
|
50
|
+
reflectionEntity?: string;
|
|
51
|
+
}): Promise<RecallResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Format recalled memories into a text block for LLM system prompt injection.
|
|
54
|
+
* Groups episodic memories by type: conversations vs events.
|
|
55
|
+
* Deduplicates semantic results that overlap with episodic results.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildMemoryBlock(episodicMemories?: Episode[], semanticMemories?: Episode[], reflections?: Reflection[]): string;
|
|
58
|
+
//# sourceMappingURL=recall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../../src/memory/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,UAAU,EAEX,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,gCAAgC;AAChC,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAgFD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,YAAY,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,iBAAiB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC,YAAY,CAAC,CA2FvB;AAcD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAC5B,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAC5B,WAAW,CAAC,EAAE,UAAU,EAAE,GACzB,MAAM,CAwCR"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared memory recall and prompt-building utilities.
|
|
4
|
+
*
|
|
5
|
+
* Used by MolrooPersona to recall episodic/semantic memories and
|
|
6
|
+
* format them for LLM system prompts. Supports both:
|
|
7
|
+
* - Single MemoryAdapter (new, preferred)
|
|
8
|
+
* - Split EpisodeAdapter + SemanticAdapter + EmbeddingAdapter (backward compat)
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.stripMetaTags = stripMetaTags;
|
|
12
|
+
exports.recallMemories = recallMemories;
|
|
13
|
+
exports.buildMemoryBlock = buildMemoryBlock;
|
|
14
|
+
/**
|
|
15
|
+
* Strip leading `[...]` metadata tags from text before embedding.
|
|
16
|
+
* These tags (channel info, scene hints, timestamps) add noise to
|
|
17
|
+
* semantic similarity and should not influence vector search.
|
|
18
|
+
*/
|
|
19
|
+
function stripMetaTags(text) {
|
|
20
|
+
return text.replace(/^\s*(\[.*?\]\s*)+/s, '').trim();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Recall from a single MemoryAdapter. The adapter handles all storage
|
|
24
|
+
* and vector search internally.
|
|
25
|
+
*/
|
|
26
|
+
async function recallFromAdapter(message, adapter, config, sourceEntity, reflectionEntity) {
|
|
27
|
+
const episodicLimit = config.episodicLimit ?? 5;
|
|
28
|
+
const semanticLimit = config.semanticLimit ?? 3;
|
|
29
|
+
const reflectionLimit = config.reflectionLimit ?? 3;
|
|
30
|
+
const minImportance = config.minImportance ?? 0;
|
|
31
|
+
// Episodic recall — conversations (filtered by sourceEntity)
|
|
32
|
+
const conversationP = adapter
|
|
33
|
+
.recall({
|
|
34
|
+
sourceEntity,
|
|
35
|
+
limit: episodicLimit,
|
|
36
|
+
minImportance,
|
|
37
|
+
...(config.types ? { type: config.types } : {}),
|
|
38
|
+
})
|
|
39
|
+
.catch(() => []);
|
|
40
|
+
// Episodic recall — non-chat events (no sourceEntity filter)
|
|
41
|
+
const hasTypeFilter = config.types && config.types.length > 0;
|
|
42
|
+
const eventP = !hasTypeFilter
|
|
43
|
+
? adapter
|
|
44
|
+
.recall({
|
|
45
|
+
limit: episodicLimit,
|
|
46
|
+
minImportance,
|
|
47
|
+
})
|
|
48
|
+
.then((all) => all.filter((ep) => ep.type && ep.type !== 'chat_message'))
|
|
49
|
+
.catch(() => [])
|
|
50
|
+
: Promise.resolve([]);
|
|
51
|
+
// Semantic recall (vector) — adapter handles embedding internally
|
|
52
|
+
const semanticP = adapter.semanticRecall
|
|
53
|
+
? adapter
|
|
54
|
+
.semanticRecall(stripMetaTags(message), {
|
|
55
|
+
topK: semanticLimit,
|
|
56
|
+
filter: { minImportance },
|
|
57
|
+
})
|
|
58
|
+
.catch(() => [])
|
|
59
|
+
: Promise.resolve([]);
|
|
60
|
+
// Reflection recall
|
|
61
|
+
const reflectionP = adapter.getReflections
|
|
62
|
+
? adapter
|
|
63
|
+
.getReflections(reflectionEntity)
|
|
64
|
+
.then((refs) => refs.slice(0, reflectionLimit))
|
|
65
|
+
.catch(() => [])
|
|
66
|
+
: Promise.resolve([]);
|
|
67
|
+
const [conversations, events, semantic, reflections] = await Promise.all([
|
|
68
|
+
conversationP,
|
|
69
|
+
eventP,
|
|
70
|
+
semanticP,
|
|
71
|
+
reflectionP,
|
|
72
|
+
]);
|
|
73
|
+
// Merge conversations + events, deduplicate, and re-sort by recency
|
|
74
|
+
const seenIds = new Set();
|
|
75
|
+
const episodic = [];
|
|
76
|
+
for (const ep of [...conversations, ...events]) {
|
|
77
|
+
if (!seenIds.has(ep.id)) {
|
|
78
|
+
seenIds.add(ep.id);
|
|
79
|
+
episodic.push(ep);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
episodic.sort((a, b) => b.timestamp - a.timestamp);
|
|
83
|
+
episodic.splice(episodicLimit);
|
|
84
|
+
return { episodic, semantic, reflections };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Recall episodic + semantic + reflection memories in parallel.
|
|
88
|
+
*
|
|
89
|
+
* Supports two paths:
|
|
90
|
+
* 1. Single MemoryAdapter (`memoryAdapter` param) — adapter handles everything
|
|
91
|
+
* 2. Split stores (`episodeStore` + `semanticStore` + `embeddingProvider`) — legacy
|
|
92
|
+
*
|
|
93
|
+
* @param message - Current user message (used for semantic embedding).
|
|
94
|
+
* @param opts - Stores, provider, config, and entity filter.
|
|
95
|
+
* @returns Recalled memories grouped by type.
|
|
96
|
+
*/
|
|
97
|
+
async function recallMemories(message, opts) {
|
|
98
|
+
const config = opts.config ?? {};
|
|
99
|
+
// Path 1: Single MemoryAdapter
|
|
100
|
+
if (opts.memoryAdapter) {
|
|
101
|
+
return recallFromAdapter(message, opts.memoryAdapter, config, opts.sourceEntity, opts.reflectionEntity);
|
|
102
|
+
}
|
|
103
|
+
// Path 2: Split stores (backward compat)
|
|
104
|
+
const { episodeStore, semanticStore, embeddingProvider } = opts;
|
|
105
|
+
const episodicLimit = config.episodicLimit ?? 5;
|
|
106
|
+
const semanticLimit = config.semanticLimit ?? 3;
|
|
107
|
+
const reflectionLimit = config.reflectionLimit ?? 3;
|
|
108
|
+
const minImportance = config.minImportance ?? 0;
|
|
109
|
+
// Episodic recall — conversations (filtered by sourceEntity)
|
|
110
|
+
const conversationP = episodeStore
|
|
111
|
+
? episodeStore
|
|
112
|
+
.recall({
|
|
113
|
+
sourceEntity: opts.sourceEntity,
|
|
114
|
+
limit: episodicLimit,
|
|
115
|
+
minImportance,
|
|
116
|
+
...(config.types ? { type: config.types } : {}),
|
|
117
|
+
})
|
|
118
|
+
.catch(() => [])
|
|
119
|
+
: Promise.resolve([]);
|
|
120
|
+
// Episodic recall — non-chat events (no sourceEntity filter).
|
|
121
|
+
// Events like attacks/gifts come from various entities and must be
|
|
122
|
+
// recalled regardless of who the persona is currently talking to.
|
|
123
|
+
const hasTypeFilter = config.types && config.types.length > 0;
|
|
124
|
+
const eventP = (episodeStore && !hasTypeFilter)
|
|
125
|
+
? episodeStore
|
|
126
|
+
.recall({
|
|
127
|
+
// No sourceEntity — recall events from all sources
|
|
128
|
+
limit: episodicLimit,
|
|
129
|
+
minImportance,
|
|
130
|
+
})
|
|
131
|
+
.then((all) => all.filter((ep) => ep.type && ep.type !== 'chat_message'))
|
|
132
|
+
.catch(() => [])
|
|
133
|
+
: Promise.resolve([]);
|
|
134
|
+
// Semantic recall (vector)
|
|
135
|
+
const semanticP = semanticStore && embeddingProvider && episodeStore?.getByIds
|
|
136
|
+
? embeddingProvider
|
|
137
|
+
.embed(stripMetaTags(message))
|
|
138
|
+
.then((embedding) => semanticStore.search({ embedding, topK: semanticLimit, filter: { minImportance } }))
|
|
139
|
+
.then((results) => results.length > 0
|
|
140
|
+
? episodeStore.getByIds(results.map((r) => r.id))
|
|
141
|
+
: [])
|
|
142
|
+
.catch(() => [])
|
|
143
|
+
: Promise.resolve([]);
|
|
144
|
+
// Reflection recall (RDB)
|
|
145
|
+
const reflectionP = episodeStore
|
|
146
|
+
? episodeStore
|
|
147
|
+
.getReflections(opts.reflectionEntity, reflectionLimit)
|
|
148
|
+
.catch(() => [])
|
|
149
|
+
: Promise.resolve([]);
|
|
150
|
+
const [conversations, events, semantic, reflections] = await Promise.all([
|
|
151
|
+
conversationP,
|
|
152
|
+
eventP,
|
|
153
|
+
semanticP,
|
|
154
|
+
reflectionP,
|
|
155
|
+
]);
|
|
156
|
+
// Merge conversations + events, deduplicate, and re-sort by recency
|
|
157
|
+
const seenIds = new Set();
|
|
158
|
+
const episodic = [];
|
|
159
|
+
for (const ep of [...conversations, ...events]) {
|
|
160
|
+
if (!seenIds.has(ep.id)) {
|
|
161
|
+
seenIds.add(ep.id);
|
|
162
|
+
episodic.push(ep);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
episodic.sort((a, b) => b.timestamp - a.timestamp);
|
|
166
|
+
episodic.splice(episodicLimit);
|
|
167
|
+
return { episodic, semantic, reflections };
|
|
168
|
+
}
|
|
169
|
+
/** Format a single episode into a display line. */
|
|
170
|
+
function formatEpisodeLine(ep) {
|
|
171
|
+
const v = ep.emotionSnapshot;
|
|
172
|
+
const isEvent = ep.type && ep.type !== 'chat_message';
|
|
173
|
+
const prefix = isEvent ? `[${ep.type}] ` : '';
|
|
174
|
+
return (`- [${new Date(ep.timestamp).toISOString()}] `
|
|
175
|
+
+ `${prefix}${ep.sourceEntity ?? 'unknown'}: ${ep.context ?? 'no context'} `
|
|
176
|
+
+ `(V:${v.V.toFixed(2)} A:${v.A.toFixed(2)} importance:${ep.importance.toFixed(2)})`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Format recalled memories into a text block for LLM system prompt injection.
|
|
180
|
+
* Groups episodic memories by type: conversations vs events.
|
|
181
|
+
* Deduplicates semantic results that overlap with episodic results.
|
|
182
|
+
*/
|
|
183
|
+
function buildMemoryBlock(episodicMemories, semanticMemories, reflections) {
|
|
184
|
+
const parts = [];
|
|
185
|
+
if (episodicMemories && episodicMemories.length > 0) {
|
|
186
|
+
const conversations = episodicMemories.filter((ep) => !ep.type || ep.type === 'chat_message');
|
|
187
|
+
const events = episodicMemories.filter((ep) => ep.type && ep.type !== 'chat_message');
|
|
188
|
+
if (conversations.length > 0) {
|
|
189
|
+
parts.push('Recent conversations:');
|
|
190
|
+
for (const ep of conversations)
|
|
191
|
+
parts.push(formatEpisodeLine(ep));
|
|
192
|
+
}
|
|
193
|
+
if (events.length > 0) {
|
|
194
|
+
if (parts.length > 0)
|
|
195
|
+
parts.push('');
|
|
196
|
+
parts.push('Recent events:');
|
|
197
|
+
for (const ep of events)
|
|
198
|
+
parts.push(formatEpisodeLine(ep));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (semanticMemories && semanticMemories.length > 0) {
|
|
202
|
+
const episodicIds = new Set(episodicMemories?.map((e) => e.id) ?? []);
|
|
203
|
+
const unique = semanticMemories.filter((e) => !episodicIds.has(e.id));
|
|
204
|
+
if (unique.length > 0) {
|
|
205
|
+
if (parts.length > 0)
|
|
206
|
+
parts.push('');
|
|
207
|
+
parts.push('Related past experiences:');
|
|
208
|
+
for (const ep of unique)
|
|
209
|
+
parts.push(formatEpisodeLine(ep));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (reflections && reflections.length > 0) {
|
|
213
|
+
if (parts.length > 0)
|
|
214
|
+
parts.push('');
|
|
215
|
+
parts.push('Your recent self-reflections:');
|
|
216
|
+
for (const ref of reflections)
|
|
217
|
+
parts.push(`- ${ref.content}`);
|
|
218
|
+
}
|
|
219
|
+
return parts.join('\n');
|
|
220
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Semantic (vector-based) memory interface. */
|
|
2
|
+
export interface SemanticMemory {
|
|
3
|
+
/** Embed and store text. Returns the stored entry ID. */
|
|
4
|
+
store(text: string, metadata?: Record<string, unknown>): Promise<string>;
|
|
5
|
+
/** Embed query and return similar entries by cosine similarity. */
|
|
6
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
7
|
+
/** Delete entries by ID. */
|
|
8
|
+
delete?(ids: string[]): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchOptions {
|
|
11
|
+
/** Maximum number of results. Default: 5. */
|
|
12
|
+
topK?: number;
|
|
13
|
+
/** Minimum similarity score threshold (0–1). */
|
|
14
|
+
scoreThreshold?: number;
|
|
15
|
+
/** Backend-specific metadata filter. */
|
|
16
|
+
filter?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface SearchResult {
|
|
19
|
+
id: string;
|
|
20
|
+
text: string;
|
|
21
|
+
score: number;
|
|
22
|
+
metadata: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=semantic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../../src/memory/semantic.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,mEAAmE;IACnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACxE,4BAA4B;IAC5B,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/memory/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC"}
|