@elizaos/plugin-memory 2.0.0-alpha.3 → 2.0.0-alpha.5
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/index.js +437 -1071
- package/dist/index.js.map +9 -14
- package/package.json +1 -1
- package/dist/evaluators/index.d.ts +0 -3
- package/dist/evaluators/index.d.ts.map +0 -1
- package/dist/evaluators/long-term-extraction.d.ts +0 -3
- package/dist/evaluators/long-term-extraction.d.ts.map +0 -1
- package/dist/evaluators/summarization.d.ts +0 -3
- package/dist/evaluators/summarization.d.ts.map +0 -1
- package/dist/generated/prompts/typescript/prompts.d.ts +0 -16
- package/dist/generated/prompts/typescript/prompts.d.ts.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/providers/context-summary.d.ts +0 -3
- package/dist/providers/context-summary.d.ts.map +0 -1
- package/dist/providers/index.d.ts +0 -3
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/long-term-memory.d.ts +0 -3
- package/dist/providers/long-term-memory.d.ts.map +0 -1
- package/dist/schemas/index.d.ts +0 -4
- package/dist/schemas/index.d.ts.map +0 -1
- package/dist/schemas/long-term-memories.d.ts +0 -261
- package/dist/schemas/long-term-memories.d.ts.map +0 -1
- package/dist/schemas/memory-access-logs.d.ts +0 -116
- package/dist/schemas/memory-access-logs.d.ts.map +0 -1
- package/dist/schemas/session-summaries.d.ts +0 -280
- package/dist/schemas/session-summaries.d.ts.map +0 -1
- package/dist/services/memory-service.d.ts +0 -34
- package/dist/services/memory-service.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -64
- package/dist/types/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1133 +1,499 @@
|
|
|
1
|
-
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/evaluators/long-term-extraction.ts
|
|
1
|
+
// src/actions/forget.ts
|
|
13
2
|
import {
|
|
14
|
-
composePromptFromState,
|
|
15
3
|
logger,
|
|
16
4
|
ModelType
|
|
17
5
|
} from "@elizaos/core";
|
|
18
6
|
|
|
19
|
-
// src/
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
# Current Long-Term Memories
|
|
58
|
-
{{existingMemories}}
|
|
59
|
-
|
|
60
|
-
# Memory Categories (Based on Cognitive Science)
|
|
61
|
-
|
|
62
|
-
## 1. EPISODIC Memory
|
|
63
|
-
Personal experiences and specific events with temporal/spatial context.
|
|
64
|
-
**Examples:**
|
|
65
|
-
- "User completed migration project from MongoDB to PostgreSQL in Q2 2024"
|
|
66
|
-
- "User encountered authentication bug in production on March 15th"
|
|
67
|
-
- "User had a negative experience with Docker networking in previous job"
|
|
68
|
-
|
|
69
|
-
**Requirements:**
|
|
70
|
-
- Must include WHO did WHAT, WHEN/WHERE
|
|
71
|
-
- Must be a specific, concrete event (not a pattern)
|
|
72
|
-
- Must have significant impact or relevance to future work
|
|
73
|
-
|
|
74
|
-
## 2. SEMANTIC Memory
|
|
75
|
-
General facts, concepts, knowledge, and established truths about the user.
|
|
76
|
-
**Examples:**
|
|
77
|
-
- "User is a senior backend engineer with 8 years experience"
|
|
78
|
-
- "User specializes in distributed systems and microservices architecture"
|
|
79
|
-
- "User's primary programming language is TypeScript"
|
|
80
|
-
- "User works at Acme Corp as technical lead"
|
|
81
|
-
|
|
82
|
-
**Requirements:**
|
|
83
|
-
- Must be factual, timeless information
|
|
84
|
-
- Must be explicitly stated or demonstrated conclusively
|
|
85
|
-
- No speculation or inference from single instances
|
|
86
|
-
- Core identity, expertise, or knowledge only
|
|
87
|
-
|
|
88
|
-
## 3. PROCEDURAL Memory
|
|
89
|
-
Skills, workflows, methodologies, and how-to knowledge.
|
|
90
|
-
**Examples:**
|
|
91
|
-
- "User follows strict TDD workflow: write tests first, then implementation"
|
|
92
|
-
- "User prefers git rebase over merge to maintain linear history"
|
|
93
|
-
- "User's debugging process: check logs → reproduce locally → binary search"
|
|
94
|
-
- "User always writes JSDoc comments before implementing functions"
|
|
95
|
-
|
|
96
|
-
**Requirements:**
|
|
97
|
-
- Must describe HOW user does something
|
|
98
|
-
- Must be a repeated, consistent pattern (seen 3+ times or explicitly stated as standard practice)
|
|
99
|
-
- Must be a workflow, methodology, or skill application
|
|
100
|
-
- Not one-off preferences
|
|
101
|
-
|
|
102
|
-
# ULTRA-STRICT EXTRACTION CRITERIA
|
|
103
|
-
|
|
104
|
-
## ✅ DO EXTRACT (Only These):
|
|
105
|
-
|
|
106
|
-
**EPISODIC:**
|
|
107
|
-
- Significant completed projects or milestones
|
|
108
|
-
- Important bugs, incidents, or problems encountered
|
|
109
|
-
- Major decisions made with lasting impact
|
|
110
|
-
- Formative experiences that shape future work
|
|
111
|
-
|
|
112
|
-
**SEMANTIC:**
|
|
113
|
-
- Professional identity (role, title, company)
|
|
114
|
-
- Core expertise and specializations (stated explicitly or demonstrated conclusively)
|
|
115
|
-
- Primary languages, frameworks, or tools (not exploratory use)
|
|
116
|
-
- Established facts about their work context
|
|
117
|
-
|
|
118
|
-
**PROCEDURAL:**
|
|
119
|
-
- Consistent workflows demonstrated 3+ times or explicitly stated
|
|
120
|
-
- Standard practices user always follows
|
|
121
|
-
- Methodology preferences with clear rationale
|
|
122
|
-
- Debugging, testing, or development processes
|
|
123
|
-
|
|
124
|
-
## ❌ NEVER EXTRACT:
|
|
125
|
-
|
|
126
|
-
- **One-time requests or tasks** (e.g., "can you generate an image", "help me debug this")
|
|
127
|
-
- **Casual conversations** without lasting significance
|
|
128
|
-
- **Exploratory questions** (e.g., "how does X work?")
|
|
129
|
-
- **Temporary context** (current bug, today's task)
|
|
130
|
-
- **Preferences from single occurrence** (e.g., user asked for code once)
|
|
131
|
-
- **Social pleasantries** (thank you, greetings)
|
|
132
|
-
- **Testing or experimentation** (trying out a feature)
|
|
133
|
-
- **Common patterns everyone has** (likes clear explanations)
|
|
134
|
-
- **Situational information** (working on feature X today)
|
|
135
|
-
- **Opinions without persistence** (single complaint, isolated praise)
|
|
136
|
-
- **General knowledge** (not specific to user)
|
|
137
|
-
|
|
138
|
-
# Quality Gates (ALL Must Pass)
|
|
139
|
-
|
|
140
|
-
1. **Significance Test**: Will this matter in 3+ months?
|
|
141
|
-
2. **Specificity Test**: Is this concrete and actionable?
|
|
142
|
-
3. **Evidence Test**: Is there strong evidence (3+ instances OR explicit self-identification)?
|
|
143
|
-
4. **Uniqueness Test**: Is this specific to THIS user (not generic)?
|
|
144
|
-
5. **Confidence Test**: Confidence must be >= 0.85 (be VERY conservative)
|
|
145
|
-
6. **Non-Redundancy Test**: Does this add NEW information not in existing memories?
|
|
146
|
-
|
|
147
|
-
# Confidence Scoring (Be Conservative)
|
|
148
|
-
|
|
149
|
-
- **0.95-1.0**: User explicitly stated as core identity/practice AND demonstrated multiple times
|
|
150
|
-
- **0.85-0.94**: User explicitly stated OR consistently demonstrated 5+ times
|
|
151
|
-
- **0.75-0.84**: Strong pattern (3-4 instances) with supporting context
|
|
152
|
-
- **Below 0.75**: DO NOT EXTRACT (insufficient evidence)
|
|
153
|
-
|
|
154
|
-
# Critical Instructions
|
|
155
|
-
|
|
156
|
-
1. **Default to NOT extracting** - When in doubt, skip it
|
|
157
|
-
2. **Require overwhelming evidence** - One or two mentions is NOT enough
|
|
158
|
-
3. **Focus on what's PERSISTENT** - Not what's temporary or situational
|
|
159
|
-
4. **Verify against existing memories** - Don't duplicate or contradict
|
|
160
|
-
5. **Maximum 2-3 extractions per run** - Quality over quantity
|
|
161
|
-
|
|
162
|
-
**If there are no qualifying facts (which is common), respond with <memories></memories>**
|
|
163
|
-
|
|
164
|
-
# Response Format
|
|
165
|
-
|
|
166
|
-
<memories>
|
|
167
|
-
<memory>
|
|
168
|
-
<category>semantic</category>
|
|
169
|
-
<content>User is a senior TypeScript developer with 8 years of backend experience</content>
|
|
170
|
-
<confidence>0.95</confidence>
|
|
171
|
-
</memory>
|
|
172
|
-
<memory>
|
|
173
|
-
<category>procedural</category>
|
|
174
|
-
<content>User follows TDD workflow: writes tests before implementation, runs tests after each change</content>
|
|
175
|
-
<confidence>0.88</confidence>
|
|
176
|
-
</memory>
|
|
177
|
-
<memory>
|
|
178
|
-
<category>episodic</category>
|
|
179
|
-
<content>User led database migration from MongoDB to PostgreSQL for payment system in Q2 2024</content>
|
|
180
|
-
<confidence>0.92</confidence>
|
|
181
|
-
</memory>
|
|
182
|
-
</memories>`;
|
|
183
|
-
var updateSummarizationTemplate = `# Task: Update and Condense Conversation Summary
|
|
184
|
-
|
|
185
|
-
You are updating an existing conversation summary with new messages, while keeping the total summary concise.
|
|
186
|
-
|
|
187
|
-
# Existing Summary
|
|
188
|
-
{{existingSummary}}
|
|
189
|
-
|
|
190
|
-
# Existing Topics
|
|
191
|
-
{{existingTopics}}
|
|
192
|
-
|
|
193
|
-
# New Messages Since Last Summary
|
|
194
|
-
{{newMessages}}
|
|
195
|
-
|
|
196
|
-
# Instructions
|
|
197
|
-
Update the summary by:
|
|
198
|
-
1. Merging the existing summary with insights from the new messages
|
|
199
|
-
2. Removing redundant or less important details to stay under the token limit
|
|
200
|
-
3. Keeping the most important context and decisions
|
|
201
|
-
4. Adding new topics if they emerge
|
|
202
|
-
5. **CRITICAL**: Keep the ENTIRE updated summary under 2500 tokens
|
|
203
|
-
|
|
204
|
-
The goal is a rolling summary that captures the essence of the conversation without growing indefinitely.
|
|
205
|
-
|
|
206
|
-
Respond in this XML format:
|
|
207
|
-
<summary>
|
|
208
|
-
<text>Your updated and condensed summary here</text>
|
|
209
|
-
<topics>topic1, topic2, topic3</topics>
|
|
210
|
-
<keyPoints>
|
|
211
|
-
<point>First key point</point>
|
|
212
|
-
<point>Second key point</point>
|
|
213
|
-
</keyPoints>
|
|
214
|
-
</summary>`;
|
|
215
|
-
|
|
216
|
-
// src/types/index.ts
|
|
217
|
-
var LongTermMemoryCategory;
|
|
218
|
-
((LongTermMemoryCategory2) => {
|
|
219
|
-
LongTermMemoryCategory2["EPISODIC"] = "episodic";
|
|
220
|
-
LongTermMemoryCategory2["SEMANTIC"] = "semantic";
|
|
221
|
-
LongTermMemoryCategory2["PROCEDURAL"] = "procedural";
|
|
222
|
-
})(LongTermMemoryCategory ||= {});
|
|
223
|
-
|
|
224
|
-
// src/evaluators/long-term-extraction.ts
|
|
225
|
-
var extractionTemplate = longTermExtractionTemplate;
|
|
226
|
-
function parseMemoryExtractionXML(xml) {
|
|
227
|
-
const memoryMatches = xml.matchAll(/<memory>[\s\S]*?<category>(.*?)<\/category>[\s\S]*?<content>(.*?)<\/content>[\s\S]*?<confidence>(.*?)<\/confidence>[\s\S]*?<\/memory>/g);
|
|
228
|
-
const extractions = [];
|
|
229
|
-
for (const match of memoryMatches) {
|
|
230
|
-
const category = match[1].trim();
|
|
231
|
-
const content = match[2].trim();
|
|
232
|
-
const confidence = parseFloat(match[3].trim());
|
|
233
|
-
if (!Object.values(LongTermMemoryCategory).includes(category)) {
|
|
234
|
-
logger.warn(`Invalid memory category: ${category}`);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
if (content && !Number.isNaN(confidence)) {
|
|
238
|
-
extractions.push({ category, content, confidence });
|
|
239
|
-
}
|
|
7
|
+
// src/types.ts
|
|
8
|
+
var MemoryImportance;
|
|
9
|
+
((MemoryImportance2) => {
|
|
10
|
+
MemoryImportance2[MemoryImportance2["LOW"] = 1] = "LOW";
|
|
11
|
+
MemoryImportance2[MemoryImportance2["NORMAL"] = 2] = "NORMAL";
|
|
12
|
+
MemoryImportance2[MemoryImportance2["HIGH"] = 3] = "HIGH";
|
|
13
|
+
MemoryImportance2[MemoryImportance2["CRITICAL"] = 4] = "CRITICAL";
|
|
14
|
+
})(MemoryImportance ||= {});
|
|
15
|
+
var MEMORY_METADATA_SEPARATOR = `
|
|
16
|
+
---
|
|
17
|
+
`;
|
|
18
|
+
var MEMORY_SOURCE = "plugin-memory";
|
|
19
|
+
var IMPORTANCE_LABELS = {
|
|
20
|
+
[1 /* LOW */]: "low",
|
|
21
|
+
[2 /* NORMAL */]: "normal",
|
|
22
|
+
[3 /* HIGH */]: "high",
|
|
23
|
+
[4 /* CRITICAL */]: "critical"
|
|
24
|
+
};
|
|
25
|
+
function encodeMemoryText(content, tags, importance) {
|
|
26
|
+
const metadata = JSON.stringify({ t: tags, i: importance });
|
|
27
|
+
return `${metadata}${MEMORY_METADATA_SEPARATOR}${content}`;
|
|
28
|
+
}
|
|
29
|
+
function decodeMemoryText(text) {
|
|
30
|
+
const separatorIndex = text.indexOf(MEMORY_METADATA_SEPARATOR);
|
|
31
|
+
if (separatorIndex === -1) {
|
|
32
|
+
return { content: text, tags: [], importance: 2 /* NORMAL */ };
|
|
33
|
+
}
|
|
34
|
+
const metadataStr = text.substring(0, separatorIndex);
|
|
35
|
+
const content = text.substring(separatorIndex + MEMORY_METADATA_SEPARATOR.length);
|
|
36
|
+
try {
|
|
37
|
+
const metadata = JSON.parse(metadataStr);
|
|
38
|
+
return {
|
|
39
|
+
content,
|
|
40
|
+
tags: Array.isArray(metadata.t) ? metadata.t.map(String) : [],
|
|
41
|
+
importance: typeof metadata.i === "number" ? metadata.i : 2 /* NORMAL */
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return { content: text, tags: [], importance: 2 /* NORMAL */ };
|
|
240
45
|
}
|
|
241
|
-
return extractions;
|
|
242
46
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (!config.longTermExtractionEnabled) {
|
|
261
|
-
logger.debug("Long-term memory extraction is disabled");
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
const currentMessageCount = await runtime.countMemories(message.roomId, false, "messages");
|
|
265
|
-
return memoryService.shouldRunExtraction(message.entityId, message.roomId, currentMessageCount);
|
|
266
|
-
},
|
|
267
|
-
handler: async (runtime, message) => {
|
|
268
|
-
const memoryService = runtime.getService("memory");
|
|
269
|
-
if (!memoryService) {
|
|
270
|
-
logger.error("MemoryService not found");
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const config = memoryService.getConfig();
|
|
274
|
-
const { entityId, roomId } = message;
|
|
275
|
-
try {
|
|
276
|
-
logger.info(`Extracting long-term memories for entity ${entityId}`);
|
|
277
|
-
const recentMessages = await runtime.getMemories({
|
|
278
|
-
tableName: "messages",
|
|
279
|
-
roomId,
|
|
280
|
-
count: 20,
|
|
281
|
-
unique: false
|
|
282
|
-
});
|
|
283
|
-
const formattedMessages = recentMessages.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0)).map((msg) => {
|
|
284
|
-
const sender = msg.entityId === runtime.agentId ? runtime.character.name : "User";
|
|
285
|
-
return `${sender}: ${msg.content.text || "[non-text message]"}`;
|
|
286
|
-
}).join(`
|
|
287
|
-
`);
|
|
288
|
-
const existingMemories = await memoryService.getLongTermMemories(entityId, undefined, 30);
|
|
289
|
-
const formattedExisting = existingMemories.length > 0 ? existingMemories.map((m) => `[${m.category}] ${m.content} (confidence: ${m.confidence})`).join(`
|
|
290
|
-
`) : "None yet";
|
|
291
|
-
const state = await runtime.composeState(message);
|
|
292
|
-
const prompt = composePromptFromState({
|
|
293
|
-
state: {
|
|
294
|
-
...state,
|
|
295
|
-
recentMessages: formattedMessages,
|
|
296
|
-
existingMemories: formattedExisting
|
|
297
|
-
},
|
|
298
|
-
template: extractionTemplate
|
|
299
|
-
});
|
|
300
|
-
const response = await runtime.useModel(ModelType.TEXT_LARGE, { prompt });
|
|
301
|
-
const extractions = parseMemoryExtractionXML(response);
|
|
302
|
-
logger.info(`Extracted ${extractions.length} long-term memories`);
|
|
303
|
-
for (const extraction of extractions) {
|
|
304
|
-
if (extraction.confidence >= Math.max(config.longTermConfidenceThreshold, 0.85)) {
|
|
305
|
-
await memoryService.storeLongTermMemory({
|
|
306
|
-
agentId: runtime.agentId,
|
|
307
|
-
entityId,
|
|
308
|
-
category: extraction.category,
|
|
309
|
-
content: extraction.content,
|
|
310
|
-
confidence: extraction.confidence,
|
|
311
|
-
source: "conversation",
|
|
312
|
-
metadata: {
|
|
313
|
-
roomId,
|
|
314
|
-
extractedAt: new Date().toISOString()
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
logger.info(`Stored long-term memory: [${extraction.category}] ${extraction.content.substring(0, 50)}...`);
|
|
318
|
-
} else {
|
|
319
|
-
logger.debug(`Skipped low-confidence memory: ${extraction.content} (confidence: ${extraction.confidence})`);
|
|
47
|
+
|
|
48
|
+
// src/actions/forget.ts
|
|
49
|
+
var forgetAction = {
|
|
50
|
+
name: "FORGET",
|
|
51
|
+
description: "Remove a stored memory by ID or by matching content description",
|
|
52
|
+
similes: ["forget", "remove-memory", "delete-memory", "erase-memory", "clear-memory"],
|
|
53
|
+
examples: [
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
name: "User",
|
|
57
|
+
content: { text: "Forget what you know about my favorite color." }
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "Assistant",
|
|
61
|
+
content: {
|
|
62
|
+
text: "I'll remove that memory about your favorite color.",
|
|
63
|
+
actions: ["FORGET"]
|
|
320
64
|
}
|
|
321
65
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
tableName: "messages",
|
|
341
|
-
roomId,
|
|
342
|
-
count: 100,
|
|
343
|
-
unique: false
|
|
344
|
-
});
|
|
345
|
-
const dialogueMessages = messages.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result") && (msg.metadata?.type === "agent_response_message" || msg.metadata?.type === "user_message"));
|
|
346
|
-
return dialogueMessages.length;
|
|
347
|
-
}
|
|
348
|
-
function parseSummaryXML(xml) {
|
|
349
|
-
const summaryMatch = xml.match(/<text>([\s\S]*?)<\/text>/);
|
|
350
|
-
const topicsMatch = xml.match(/<topics>([\s\S]*?)<\/topics>/);
|
|
351
|
-
const keyPointsMatches = xml.matchAll(/<point>([\s\S]*?)<\/point>/g);
|
|
352
|
-
const summary = summaryMatch ? summaryMatch[1].trim() : "Summary not available";
|
|
353
|
-
const topics = topicsMatch ? topicsMatch[1].split(",").map((t) => t.trim()).filter(Boolean) : [];
|
|
354
|
-
const keyPoints = Array.from(keyPointsMatches).map((match) => match[1].trim());
|
|
355
|
-
return { summary, topics, keyPoints };
|
|
356
|
-
}
|
|
357
|
-
var summarizationEvaluator = {
|
|
358
|
-
name: "MEMORY_SUMMARIZATION",
|
|
359
|
-
description: "Automatically summarizes conversations to optimize context usage",
|
|
360
|
-
similes: ["CONVERSATION_SUMMARY", "CONTEXT_COMPRESSION", "MEMORY_OPTIMIZATION"],
|
|
361
|
-
alwaysRun: true,
|
|
362
|
-
validate: async (runtime, message) => {
|
|
363
|
-
if (!message.content?.text) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
const memoryService = runtime.getService("memory");
|
|
367
|
-
if (!memoryService) {
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
const config = memoryService.getConfig();
|
|
371
|
-
const currentDialogueCount = await getDialogueMessageCount(runtime, message.roomId);
|
|
372
|
-
const existingSummary = await memoryService.getCurrentSessionSummary(message.roomId);
|
|
373
|
-
if (!existingSummary) {
|
|
374
|
-
return currentDialogueCount >= config.shortTermSummarizationThreshold;
|
|
375
|
-
} else {
|
|
376
|
-
const newDialogueCount = currentDialogueCount - existingSummary.lastMessageOffset;
|
|
377
|
-
return newDialogueCount >= config.shortTermSummarizationInterval;
|
|
378
|
-
}
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
{
|
|
69
|
+
name: "User",
|
|
70
|
+
content: { text: "Delete the memory about the project deadline." }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "Assistant",
|
|
74
|
+
content: {
|
|
75
|
+
text: "I've removed the memory about the project deadline.",
|
|
76
|
+
actions: ["FORGET"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
],
|
|
81
|
+
async validate(runtime, _message, _state) {
|
|
82
|
+
const memoryManager = runtime.getMemoryManager();
|
|
83
|
+
return !!memoryManager;
|
|
379
84
|
},
|
|
380
|
-
|
|
381
|
-
const memoryService = runtime.getService("memory");
|
|
382
|
-
if (!memoryService) {
|
|
383
|
-
logger2.error("MemoryService not found");
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
const config = memoryService.getConfig();
|
|
387
|
-
const { roomId } = message;
|
|
85
|
+
async handler(runtime, message, _state, _options, callback) {
|
|
388
86
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const allMessages = await runtime.getMemories({
|
|
393
|
-
tableName: "messages",
|
|
394
|
-
roomId,
|
|
395
|
-
count: 1000,
|
|
396
|
-
unique: false
|
|
397
|
-
});
|
|
398
|
-
const allDialogueMessages = allMessages.filter((msg) => !(msg.content?.type === "action_result" && msg.metadata?.type === "action_result") && (msg.metadata?.type === "agent_response_message" || msg.metadata?.type === "user_message"));
|
|
399
|
-
const totalDialogueCount = allDialogueMessages.length;
|
|
400
|
-
const newDialogueCount = totalDialogueCount - lastOffset;
|
|
401
|
-
if (newDialogueCount === 0) {
|
|
402
|
-
logger2.debug("No new dialogue messages to summarize");
|
|
403
|
-
return;
|
|
87
|
+
const memoryManager = runtime.getMemoryManager();
|
|
88
|
+
if (!memoryManager) {
|
|
89
|
+
throw new Error("Memory manager not available");
|
|
404
90
|
}
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
91
|
+
const content = message.content.text;
|
|
92
|
+
if (!content) {
|
|
93
|
+
const errorMessage = "Please specify which memory to forget.";
|
|
94
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
95
|
+
return { text: errorMessage, success: false };
|
|
409
96
|
}
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
97
|
+
const params = _options?.parameters;
|
|
98
|
+
if (params?.memoryId) {
|
|
99
|
+
await memoryManager.removeMemory(params.memoryId);
|
|
100
|
+
const successMessage2 = `Removed memory with ID: ${params.memoryId}`;
|
|
101
|
+
await callback?.({ text: successMessage2, source: message.content.source });
|
|
102
|
+
return { text: successMessage2, success: true, data: { removedId: params.memoryId } };
|
|
415
103
|
}
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
template = updateSummarizationTemplate;
|
|
426
|
-
prompt = composePromptFromState2({
|
|
427
|
-
state: {
|
|
428
|
-
...state,
|
|
429
|
-
existingSummary: existingSummary.summary,
|
|
430
|
-
existingTopics: existingSummary.topics?.join(", ") || "None",
|
|
431
|
-
newMessages: formattedMessages
|
|
432
|
-
},
|
|
433
|
-
template
|
|
434
|
-
});
|
|
435
|
-
} else {
|
|
436
|
-
const initialMessages = sortedDialogueMessages.map((msg) => {
|
|
437
|
-
const sender = msg.entityId === runtime.agentId ? runtime.character.name : "User";
|
|
438
|
-
return `${sender}: ${msg.content.text || "[non-text message]"}`;
|
|
439
|
-
}).join(`
|
|
440
|
-
`);
|
|
441
|
-
template = initialSummarizationTemplate;
|
|
442
|
-
prompt = composePromptFromState2({
|
|
443
|
-
state: { ...state, recentMessages: initialMessages },
|
|
444
|
-
template
|
|
445
|
-
});
|
|
104
|
+
const memories = await memoryManager.getMemories({
|
|
105
|
+
roomId: message.roomId,
|
|
106
|
+
count: 100
|
|
107
|
+
});
|
|
108
|
+
const pluginMemories = memories.filter((m) => m.content.source === MEMORY_SOURCE);
|
|
109
|
+
if (pluginMemories.length === 0) {
|
|
110
|
+
const noMemoriesMsg = "No stored memories found to remove.";
|
|
111
|
+
await callback?.({ text: noMemoriesMsg, source: message.content.source });
|
|
112
|
+
return { text: noMemoriesMsg, success: true };
|
|
446
113
|
}
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
114
|
+
const searchContent = params?.content ?? content;
|
|
115
|
+
const memoryDescriptions = pluginMemories.map((m, i) => {
|
|
116
|
+
const parsed2 = decodeMemoryText(m.content.text);
|
|
117
|
+
return `${i}: "${parsed2.content}"`;
|
|
450
118
|
});
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
summary: summaryResult.summary,
|
|
474
|
-
messageCount: totalDialogueCount,
|
|
475
|
-
lastMessageOffset: totalDialogueCount,
|
|
476
|
-
startTime,
|
|
477
|
-
endTime,
|
|
478
|
-
topics: summaryResult.topics,
|
|
479
|
-
metadata: { keyPoints: summaryResult.keyPoints }
|
|
480
|
-
});
|
|
481
|
-
logger2.info(`Created summary for room ${roomId}: ${totalDialogueCount} messages summarized`);
|
|
119
|
+
const matchPrompt = `Given the user's request to forget a memory, identify which stored memory index matches best.
|
|
120
|
+
|
|
121
|
+
User request: "${searchContent}"
|
|
122
|
+
|
|
123
|
+
Available memories:
|
|
124
|
+
${memoryDescriptions.join(`
|
|
125
|
+
`)}
|
|
126
|
+
|
|
127
|
+
Return ONLY a JSON object (no markdown, no code blocks):
|
|
128
|
+
{"index": <number or -1 if no match>, "confidence": <0.0 to 1.0>}`;
|
|
129
|
+
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
130
|
+
prompt: matchPrompt
|
|
131
|
+
});
|
|
132
|
+
if (!response) {
|
|
133
|
+
throw new Error("Failed to identify memory to remove");
|
|
134
|
+
}
|
|
135
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
136
|
+
const match = JSON.parse(cleaned);
|
|
137
|
+
if (match.index < 0 || match.index >= pluginMemories.length || match.confidence < 0.5) {
|
|
138
|
+
const noMatchMsg = "Could not find a matching memory to remove. Please be more specific.";
|
|
139
|
+
await callback?.({ text: noMatchMsg, source: message.content.source });
|
|
140
|
+
return { text: noMatchMsg, success: false };
|
|
482
141
|
}
|
|
142
|
+
const targetMemory = pluginMemories[match.index];
|
|
143
|
+
const parsed = decodeMemoryText(targetMemory.content.text);
|
|
144
|
+
const memoryId = targetMemory.id ?? "";
|
|
145
|
+
if (memoryId) {
|
|
146
|
+
await memoryManager.removeMemory(memoryId);
|
|
147
|
+
}
|
|
148
|
+
const successMessage = `Removed memory: "${parsed.content}"`;
|
|
149
|
+
await callback?.({ text: successMessage, source: message.content.source });
|
|
150
|
+
return {
|
|
151
|
+
text: successMessage,
|
|
152
|
+
success: true,
|
|
153
|
+
data: { removedId: memoryId, content: parsed.content }
|
|
154
|
+
};
|
|
483
155
|
} catch (error) {
|
|
484
|
-
|
|
156
|
+
logger.error("Failed to forget memory:", error);
|
|
157
|
+
const errorMessage = `Failed to forget memory: ${error instanceof Error ? error.message : String(error)}`;
|
|
158
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
159
|
+
return { text: errorMessage, success: false };
|
|
485
160
|
}
|
|
486
|
-
|
|
487
|
-
},
|
|
488
|
-
examples: []
|
|
161
|
+
}
|
|
489
162
|
};
|
|
490
|
-
|
|
163
|
+
|
|
164
|
+
// src/actions/recall.ts
|
|
491
165
|
import {
|
|
492
|
-
|
|
493
|
-
logger as logger3
|
|
166
|
+
logger as logger2
|
|
494
167
|
} from "@elizaos/core";
|
|
495
|
-
var
|
|
496
|
-
name: "
|
|
497
|
-
description: "
|
|
498
|
-
|
|
499
|
-
|
|
168
|
+
var recallAction = {
|
|
169
|
+
name: "RECALL",
|
|
170
|
+
description: "Retrieve stored memories based on a query, tags, or topic",
|
|
171
|
+
similes: ["recall", "remember-what", "search-memory", "find-memory", "what-do-you-remember"],
|
|
172
|
+
examples: [
|
|
173
|
+
[
|
|
174
|
+
{
|
|
175
|
+
name: "User",
|
|
176
|
+
content: { text: "What do you remember about my preferences?" }
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "Assistant",
|
|
180
|
+
content: {
|
|
181
|
+
text: "Let me search my memories about your preferences.",
|
|
182
|
+
actions: ["RECALL"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
{
|
|
188
|
+
name: "User",
|
|
189
|
+
content: { text: "Recall everything about the project deadline." }
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "Assistant",
|
|
193
|
+
content: {
|
|
194
|
+
text: "I'll look up what I know about the project deadline.",
|
|
195
|
+
actions: ["RECALL"]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
],
|
|
200
|
+
async validate(runtime, _message, _state) {
|
|
201
|
+
const memoryManager = runtime.getMemoryManager();
|
|
202
|
+
return !!memoryManager;
|
|
203
|
+
},
|
|
204
|
+
async handler(runtime, message, _state, _options, callback) {
|
|
500
205
|
try {
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
};
|
|
206
|
+
const memoryManager = runtime.getMemoryManager();
|
|
207
|
+
if (!memoryManager) {
|
|
208
|
+
throw new Error("Memory manager not available");
|
|
209
|
+
}
|
|
210
|
+
const content = message.content.text;
|
|
211
|
+
if (!content) {
|
|
212
|
+
const errorMessage = "Please provide a query to recall memories.";
|
|
213
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
214
|
+
return { text: errorMessage, success: false };
|
|
509
215
|
}
|
|
510
|
-
const
|
|
511
|
-
|
|
216
|
+
const params = _options?.parameters;
|
|
217
|
+
const query = params?.query ?? content;
|
|
218
|
+
const filterTags = params?.tags ?? [];
|
|
219
|
+
const limit = params?.limit ?? 10;
|
|
220
|
+
const minImportance = params?.minImportance ?? 1 /* LOW */;
|
|
221
|
+
const memories = await memoryManager.getMemories({
|
|
222
|
+
roomId: message.roomId,
|
|
223
|
+
count: 100
|
|
224
|
+
});
|
|
225
|
+
const pluginMemories = memories.filter((m) => m.content.source === MEMORY_SOURCE);
|
|
226
|
+
if (pluginMemories.length === 0) {
|
|
227
|
+
const noMemoriesMsg = "I don't have any stored memories yet.";
|
|
228
|
+
await callback?.({ text: noMemoriesMsg, source: message.content.source });
|
|
229
|
+
return { text: noMemoriesMsg, success: true, data: { memories: [], count: 0 } };
|
|
230
|
+
}
|
|
231
|
+
const parsedMemories = pluginMemories.map((m) => {
|
|
232
|
+
const parsed = decodeMemoryText(m.content.text);
|
|
512
233
|
return {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
234
|
+
id: m.id ?? "",
|
|
235
|
+
content: parsed.content,
|
|
236
|
+
tags: parsed.tags,
|
|
237
|
+
importance: parsed.importance,
|
|
238
|
+
createdAt: m.createdAt ?? 0
|
|
516
239
|
};
|
|
240
|
+
}).filter((m) => m.importance >= minImportance);
|
|
241
|
+
let filteredMemories = parsedMemories;
|
|
242
|
+
if (filterTags.length > 0) {
|
|
243
|
+
filteredMemories = parsedMemories.filter((m) => filterTags.some((tag) => m.tags.includes(tag)));
|
|
517
244
|
}
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
245
|
+
const queryLower = query.toLowerCase();
|
|
246
|
+
const queryWords = queryLower.split(/\s+/);
|
|
247
|
+
const scoredMemories = filteredMemories.map((m) => {
|
|
248
|
+
const contentLower = m.content.toLowerCase();
|
|
249
|
+
const tagsStr = m.tags.join(" ").toLowerCase();
|
|
250
|
+
let score = 0;
|
|
251
|
+
if (contentLower.includes(queryLower)) {
|
|
252
|
+
score += 10;
|
|
253
|
+
}
|
|
254
|
+
for (const word of queryWords) {
|
|
255
|
+
if (word.length < 2)
|
|
256
|
+
continue;
|
|
257
|
+
if (contentLower.includes(word))
|
|
258
|
+
score += 2;
|
|
259
|
+
if (tagsStr.includes(word))
|
|
260
|
+
score += 3;
|
|
261
|
+
}
|
|
262
|
+
score += m.importance;
|
|
263
|
+
return { ...m, score };
|
|
264
|
+
}).filter((m) => m.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
265
|
+
if (scoredMemories.length === 0) {
|
|
266
|
+
const noResultsMsg = "No memories found matching your query.";
|
|
267
|
+
await callback?.({ text: noResultsMsg, source: message.content.source });
|
|
268
|
+
return { text: noResultsMsg, success: true, data: { memories: [], count: 0 } };
|
|
527
269
|
}
|
|
528
|
-
const
|
|
529
|
-
|
|
270
|
+
const memoryList = scoredMemories.map((m, i) => {
|
|
271
|
+
const tagStr = m.tags.length > 0 ? ` [${m.tags.join(", ")}]` : "";
|
|
272
|
+
const date = new Date(m.createdAt).toLocaleDateString();
|
|
273
|
+
return `${i + 1}. ${m.content}${tagStr} (${date})`;
|
|
274
|
+
}).join(`
|
|
275
|
+
`);
|
|
276
|
+
const count = scoredMemories.length;
|
|
277
|
+
const resultText = `Found ${count} memor${count === 1 ? "y" : "ies"}:
|
|
278
|
+
|
|
279
|
+
${memoryList}`;
|
|
280
|
+
await callback?.({ text: resultText, source: message.content.source });
|
|
530
281
|
return {
|
|
282
|
+
text: resultText,
|
|
283
|
+
success: true,
|
|
531
284
|
data: {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
285
|
+
memories: scoredMemories.map((m) => ({
|
|
286
|
+
id: m.id,
|
|
287
|
+
content: m.content,
|
|
288
|
+
tags: m.tags,
|
|
289
|
+
importance: m.importance,
|
|
290
|
+
createdAt: m.createdAt
|
|
291
|
+
})),
|
|
292
|
+
count
|
|
293
|
+
}
|
|
538
294
|
};
|
|
539
295
|
} catch (error) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
text: ""
|
|
545
|
-
};
|
|
296
|
+
logger2.error("Failed to recall memories:", error);
|
|
297
|
+
const errorMessage = `Failed to recall memories: ${error instanceof Error ? error.message : String(error)}`;
|
|
298
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
299
|
+
return { text: errorMessage, success: false };
|
|
546
300
|
}
|
|
547
301
|
}
|
|
548
302
|
};
|
|
549
|
-
|
|
303
|
+
|
|
304
|
+
// src/actions/remember.ts
|
|
550
305
|
import {
|
|
551
|
-
|
|
552
|
-
|
|
306
|
+
logger as logger3,
|
|
307
|
+
ModelType as ModelType2
|
|
553
308
|
} from "@elizaos/core";
|
|
554
|
-
var
|
|
555
|
-
name: "
|
|
556
|
-
description: "
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
309
|
+
var rememberAction = {
|
|
310
|
+
name: "REMEMBER",
|
|
311
|
+
description: "Store a piece of information as a long-term memory for later recall",
|
|
312
|
+
similes: ["remember", "memorize", "store-memory", "save-memory", "note-down"],
|
|
313
|
+
examples: [
|
|
314
|
+
[
|
|
315
|
+
{
|
|
316
|
+
name: "User",
|
|
317
|
+
content: { text: "Remember that my favorite color is blue." }
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "Assistant",
|
|
321
|
+
content: {
|
|
322
|
+
text: "I'll remember that your favorite color is blue.",
|
|
323
|
+
actions: ["REMEMBER"]
|
|
324
|
+
}
|
|
567
325
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
text: ""
|
|
574
|
-
}
|
|
326
|
+
],
|
|
327
|
+
[
|
|
328
|
+
{
|
|
329
|
+
name: "User",
|
|
330
|
+
content: {
|
|
331
|
+
text: "Please memorize this: the project deadline is March 15th."
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "Assistant",
|
|
336
|
+
content: {
|
|
337
|
+
text: "Got it, I've stored that the project deadline is March 15th.",
|
|
338
|
+
actions: ["REMEMBER"]
|
|
339
|
+
}
|
|
575
340
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
341
|
+
]
|
|
342
|
+
],
|
|
343
|
+
async validate(runtime, _message, _state) {
|
|
344
|
+
const memoryManager = runtime.getMemoryManager();
|
|
345
|
+
return !!memoryManager;
|
|
346
|
+
},
|
|
347
|
+
async handler(runtime, message, _state, _options, callback) {
|
|
348
|
+
try {
|
|
349
|
+
const memoryManager = runtime.getMemoryManager();
|
|
350
|
+
if (!memoryManager) {
|
|
351
|
+
throw new Error("Memory manager not available");
|
|
583
352
|
}
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
categoryCounts.set(memory.category, count + 1);
|
|
353
|
+
const content = message.content.text;
|
|
354
|
+
if (!content) {
|
|
355
|
+
const errorMessage = "Please provide content to remember.";
|
|
356
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
357
|
+
return { text: errorMessage, success: false };
|
|
590
358
|
}
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
359
|
+
const params = _options?.parameters;
|
|
360
|
+
let memoryText = params?.content ?? content;
|
|
361
|
+
let tags = params?.tags ?? [];
|
|
362
|
+
let importance = params?.importance ?? 2 /* NORMAL */;
|
|
363
|
+
if (!params?.content) {
|
|
364
|
+
const extractionPrompt = `Extract the key information to remember from this message.
|
|
365
|
+
Return ONLY a JSON object (no markdown, no code blocks):
|
|
366
|
+
{
|
|
367
|
+
"memory": "The concise fact or information to store",
|
|
368
|
+
"tags": ["relevant", "category", "tags"],
|
|
369
|
+
"importance": 2
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
Importance levels: 1=low, 2=normal, 3=high, 4=critical
|
|
373
|
+
|
|
374
|
+
User message: "${content}"`;
|
|
375
|
+
const response = await runtime.useModel(ModelType2.TEXT_LARGE, {
|
|
376
|
+
prompt: extractionPrompt
|
|
377
|
+
});
|
|
378
|
+
if (response) {
|
|
379
|
+
try {
|
|
380
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
381
|
+
const parsed = JSON.parse(cleaned);
|
|
382
|
+
memoryText = parsed.memory ?? content;
|
|
383
|
+
tags = Array.isArray(parsed.tags) ? parsed.tags.map(String) : [];
|
|
384
|
+
importance = typeof parsed.importance === "number" ? parsed.importance : 2 /* NORMAL */;
|
|
385
|
+
} catch (parseError) {
|
|
386
|
+
logger3.warn("Failed to parse memory extraction, using raw content", parseError);
|
|
387
|
+
memoryText = content;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const encodedText = encodeMemoryText(memoryText, tags, importance);
|
|
392
|
+
const memoryEntry = {
|
|
393
|
+
agentId: runtime.agentId,
|
|
394
|
+
roomId: message.roomId,
|
|
395
|
+
userId: message.userId,
|
|
396
|
+
content: {
|
|
397
|
+
text: encodedText,
|
|
398
|
+
source: MEMORY_SOURCE
|
|
600
399
|
},
|
|
601
|
-
|
|
400
|
+
createdAt: Date.now()
|
|
602
401
|
};
|
|
603
|
-
|
|
604
|
-
|
|
402
|
+
await memoryManager.createMemory(memoryEntry, true);
|
|
403
|
+
const tagSuffix = tags.length > 0 ? ` [tags: ${tags.join(", ")}]` : "";
|
|
404
|
+
const successMessage = `Remembered: "${memoryText}"${tagSuffix}`;
|
|
405
|
+
await callback?.({ text: successMessage, source: message.content.source });
|
|
605
406
|
return {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
407
|
+
text: successMessage,
|
|
408
|
+
success: true,
|
|
409
|
+
data: { content: memoryText, tags, importance }
|
|
609
410
|
};
|
|
411
|
+
} catch (error) {
|
|
412
|
+
logger3.error("Failed to store memory:", error);
|
|
413
|
+
const errorMessage = `Failed to store memory: ${error instanceof Error ? error.message : String(error)}`;
|
|
414
|
+
await callback?.({ text: errorMessage, source: message.content.source });
|
|
415
|
+
return { text: errorMessage, success: false };
|
|
610
416
|
}
|
|
611
417
|
}
|
|
612
418
|
};
|
|
613
|
-
// src/schemas/index.ts
|
|
614
|
-
var exports_schemas = {};
|
|
615
|
-
__export(exports_schemas, {
|
|
616
|
-
sessionSummaries: () => sessionSummaries,
|
|
617
|
-
memoryAccessLogs: () => memoryAccessLogs,
|
|
618
|
-
longTermMemories: () => longTermMemories
|
|
619
|
-
});
|
|
620
419
|
|
|
621
|
-
// src/
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
jsonb,
|
|
627
|
-
pgTable,
|
|
628
|
-
real,
|
|
629
|
-
text,
|
|
630
|
-
timestamp,
|
|
631
|
-
varchar
|
|
632
|
-
} from "drizzle-orm/pg-core";
|
|
633
|
-
var longTermMemories = pgTable("long_term_memories", {
|
|
634
|
-
id: varchar("id", { length: 36 }).primaryKey(),
|
|
635
|
-
agentId: varchar("agent_id", { length: 36 }).notNull(),
|
|
636
|
-
entityId: varchar("entity_id", { length: 36 }).notNull(),
|
|
637
|
-
category: text("category").notNull(),
|
|
638
|
-
content: text("content").notNull(),
|
|
639
|
-
metadata: jsonb("metadata"),
|
|
640
|
-
embedding: real("embedding").array(),
|
|
641
|
-
confidence: real("confidence").default(1),
|
|
642
|
-
source: text("source"),
|
|
643
|
-
createdAt: timestamp("created_at").default(sql`now()`).notNull(),
|
|
644
|
-
updatedAt: timestamp("updated_at").default(sql`now()`).notNull(),
|
|
645
|
-
lastAccessedAt: timestamp("last_accessed_at"),
|
|
646
|
-
accessCount: integer("access_count").default(0)
|
|
647
|
-
}, (table) => ({
|
|
648
|
-
agentEntityIdx: index("long_term_memories_agent_entity_idx").on(table.agentId, table.entityId),
|
|
649
|
-
categoryIdx: index("long_term_memories_category_idx").on(table.category),
|
|
650
|
-
confidenceIdx: index("long_term_memories_confidence_idx").on(table.confidence),
|
|
651
|
-
createdAtIdx: index("long_term_memories_created_at_idx").on(table.createdAt)
|
|
652
|
-
}));
|
|
653
|
-
// src/schemas/memory-access-logs.ts
|
|
654
|
-
import { sql as sql2 } from "drizzle-orm";
|
|
655
|
-
import { index as index2, pgTable as pgTable2, text as text2, timestamp as timestamp2, varchar as varchar2 } from "drizzle-orm/pg-core";
|
|
656
|
-
var memoryAccessLogs = pgTable2("memory_access_logs", {
|
|
657
|
-
id: varchar2("id", { length: 36 }).primaryKey(),
|
|
658
|
-
memoryId: varchar2("memory_id", { length: 36 }).notNull(),
|
|
659
|
-
memoryType: text2("memory_type").notNull(),
|
|
660
|
-
agentId: varchar2("agent_id", { length: 36 }).notNull(),
|
|
661
|
-
accessType: text2("access_type").notNull(),
|
|
662
|
-
accessedAt: timestamp2("accessed_at").default(sql2`now()`).notNull()
|
|
663
|
-
}, (table) => ({
|
|
664
|
-
memoryIdIdx: index2("memory_access_logs_memory_id_idx").on(table.memoryId),
|
|
665
|
-
agentIdIdx: index2("memory_access_logs_agent_id_idx").on(table.agentId),
|
|
666
|
-
accessedAtIdx: index2("memory_access_logs_accessed_at_idx").on(table.accessedAt)
|
|
667
|
-
}));
|
|
668
|
-
// src/schemas/session-summaries.ts
|
|
669
|
-
import { sql as sql3 } from "drizzle-orm";
|
|
670
|
-
import {
|
|
671
|
-
index as index3,
|
|
672
|
-
integer as integer2,
|
|
673
|
-
jsonb as jsonb2,
|
|
674
|
-
pgTable as pgTable3,
|
|
675
|
-
real as real2,
|
|
676
|
-
text as text3,
|
|
677
|
-
timestamp as timestamp3,
|
|
678
|
-
varchar as varchar3
|
|
679
|
-
} from "drizzle-orm/pg-core";
|
|
680
|
-
var sessionSummaries = pgTable3("session_summaries", {
|
|
681
|
-
id: varchar3("id", { length: 36 }).primaryKey(),
|
|
682
|
-
agentId: varchar3("agent_id", { length: 36 }).notNull(),
|
|
683
|
-
roomId: varchar3("room_id", { length: 36 }).notNull(),
|
|
684
|
-
entityId: varchar3("entity_id", { length: 36 }),
|
|
685
|
-
summary: text3("summary").notNull(),
|
|
686
|
-
messageCount: integer2("message_count").notNull(),
|
|
687
|
-
lastMessageOffset: integer2("last_message_offset").notNull().default(0),
|
|
688
|
-
startTime: timestamp3("start_time").notNull(),
|
|
689
|
-
endTime: timestamp3("end_time").notNull(),
|
|
690
|
-
topics: jsonb2("topics"),
|
|
691
|
-
metadata: jsonb2("metadata"),
|
|
692
|
-
embedding: real2("embedding").array(),
|
|
693
|
-
createdAt: timestamp3("created_at").default(sql3`now()`).notNull(),
|
|
694
|
-
updatedAt: timestamp3("updated_at").default(sql3`now()`).notNull()
|
|
695
|
-
}, (table) => ({
|
|
696
|
-
agentRoomIdx: index3("session_summaries_agent_room_idx").on(table.agentId, table.roomId),
|
|
697
|
-
entityIdx: index3("session_summaries_entity_idx").on(table.entityId),
|
|
698
|
-
startTimeIdx: index3("session_summaries_start_time_idx").on(table.startTime)
|
|
699
|
-
}));
|
|
700
|
-
// src/services/memory-service.ts
|
|
701
|
-
import {
|
|
702
|
-
logger as logger5,
|
|
703
|
-
Service
|
|
704
|
-
} from "@elizaos/core";
|
|
705
|
-
import { and, cosineDistance, desc, eq, gte, sql as sql4 } from "drizzle-orm";
|
|
706
|
-
class MemoryService extends Service {
|
|
707
|
-
static serviceType = "memory";
|
|
708
|
-
sessionMessageCounts;
|
|
709
|
-
memoryConfig;
|
|
710
|
-
lastExtractionCheckpoints;
|
|
711
|
-
capabilityDescription = "Memory management with short-term summarization and long-term persistent facts";
|
|
712
|
-
constructor(runtime) {
|
|
713
|
-
super(runtime);
|
|
714
|
-
this.sessionMessageCounts = new Map;
|
|
715
|
-
this.lastExtractionCheckpoints = new Map;
|
|
716
|
-
this.memoryConfig = {
|
|
717
|
-
shortTermSummarizationThreshold: 16,
|
|
718
|
-
shortTermRetainRecent: 6,
|
|
719
|
-
shortTermSummarizationInterval: 10,
|
|
720
|
-
longTermExtractionEnabled: true,
|
|
721
|
-
longTermVectorSearchEnabled: false,
|
|
722
|
-
longTermConfidenceThreshold: 0.85,
|
|
723
|
-
longTermExtractionThreshold: 30,
|
|
724
|
-
longTermExtractionInterval: 10,
|
|
725
|
-
summaryModelType: "TEXT_LARGE",
|
|
726
|
-
summaryMaxTokens: 2500,
|
|
727
|
-
summaryMaxNewMessages: 20
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
static async start(runtime) {
|
|
731
|
-
const service = new MemoryService(runtime);
|
|
732
|
-
await service.initialize(runtime);
|
|
733
|
-
return service;
|
|
734
|
-
}
|
|
735
|
-
async stop() {
|
|
736
|
-
logger5.info("MemoryService stopped");
|
|
737
|
-
}
|
|
738
|
-
async initialize(runtime) {
|
|
739
|
-
this.runtime = runtime;
|
|
740
|
-
const threshold = runtime.getSetting("MEMORY_SUMMARIZATION_THRESHOLD");
|
|
741
|
-
if (threshold) {
|
|
742
|
-
this.memoryConfig.shortTermSummarizationThreshold = parseInt(String(threshold), 10);
|
|
743
|
-
}
|
|
744
|
-
const retainRecent = runtime.getSetting("MEMORY_RETAIN_RECENT");
|
|
745
|
-
if (retainRecent) {
|
|
746
|
-
this.memoryConfig.shortTermRetainRecent = parseInt(String(retainRecent), 10);
|
|
747
|
-
}
|
|
748
|
-
const summarizationInterval = runtime.getSetting("MEMORY_SUMMARIZATION_INTERVAL");
|
|
749
|
-
if (summarizationInterval) {
|
|
750
|
-
this.memoryConfig.shortTermSummarizationInterval = parseInt(String(summarizationInterval), 10);
|
|
751
|
-
}
|
|
752
|
-
const maxNewMessages = runtime.getSetting("MEMORY_MAX_NEW_MESSAGES");
|
|
753
|
-
if (maxNewMessages) {
|
|
754
|
-
this.memoryConfig.summaryMaxNewMessages = parseInt(String(maxNewMessages), 10);
|
|
755
|
-
}
|
|
756
|
-
const longTermEnabled = runtime.getSetting("MEMORY_LONG_TERM_ENABLED");
|
|
757
|
-
if (longTermEnabled === "false" || longTermEnabled === false) {
|
|
758
|
-
this.memoryConfig.longTermExtractionEnabled = false;
|
|
759
|
-
} else if (longTermEnabled === "true" || longTermEnabled === true) {
|
|
760
|
-
this.memoryConfig.longTermExtractionEnabled = true;
|
|
761
|
-
}
|
|
762
|
-
const confidenceThreshold = runtime.getSetting("MEMORY_CONFIDENCE_THRESHOLD");
|
|
763
|
-
if (confidenceThreshold) {
|
|
764
|
-
this.memoryConfig.longTermConfidenceThreshold = parseFloat(String(confidenceThreshold));
|
|
765
|
-
}
|
|
766
|
-
const extractionThreshold = runtime.getSetting("MEMORY_EXTRACTION_THRESHOLD");
|
|
767
|
-
if (extractionThreshold) {
|
|
768
|
-
this.memoryConfig.longTermExtractionThreshold = parseInt(String(extractionThreshold), 10);
|
|
769
|
-
}
|
|
770
|
-
const extractionInterval = runtime.getSetting("MEMORY_EXTRACTION_INTERVAL");
|
|
771
|
-
if (extractionInterval) {
|
|
772
|
-
this.memoryConfig.longTermExtractionInterval = parseInt(String(extractionInterval), 10);
|
|
773
|
-
}
|
|
774
|
-
logger5.debug({
|
|
775
|
-
summarizationThreshold: this.memoryConfig.shortTermSummarizationThreshold,
|
|
776
|
-
summarizationInterval: this.memoryConfig.shortTermSummarizationInterval,
|
|
777
|
-
maxNewMessages: this.memoryConfig.summaryMaxNewMessages,
|
|
778
|
-
retainRecent: this.memoryConfig.shortTermRetainRecent,
|
|
779
|
-
longTermEnabled: this.memoryConfig.longTermExtractionEnabled,
|
|
780
|
-
extractionThreshold: this.memoryConfig.longTermExtractionThreshold,
|
|
781
|
-
extractionInterval: this.memoryConfig.longTermExtractionInterval,
|
|
782
|
-
confidenceThreshold: this.memoryConfig.longTermConfidenceThreshold
|
|
783
|
-
}, "MemoryService initialized");
|
|
784
|
-
}
|
|
785
|
-
getDb() {
|
|
786
|
-
const db = this.runtime.db;
|
|
787
|
-
if (!db) {
|
|
788
|
-
throw new Error("Database not available");
|
|
789
|
-
}
|
|
790
|
-
return db;
|
|
791
|
-
}
|
|
792
|
-
getConfig() {
|
|
793
|
-
return { ...this.memoryConfig };
|
|
794
|
-
}
|
|
795
|
-
updateConfig(updates) {
|
|
796
|
-
this.memoryConfig = { ...this.memoryConfig, ...updates };
|
|
797
|
-
}
|
|
798
|
-
incrementMessageCount(roomId) {
|
|
799
|
-
const current = this.sessionMessageCounts.get(roomId) || 0;
|
|
800
|
-
const newCount = current + 1;
|
|
801
|
-
this.sessionMessageCounts.set(roomId, newCount);
|
|
802
|
-
return newCount;
|
|
803
|
-
}
|
|
804
|
-
resetMessageCount(roomId) {
|
|
805
|
-
this.sessionMessageCounts.set(roomId, 0);
|
|
806
|
-
}
|
|
807
|
-
async shouldSummarize(roomId) {
|
|
808
|
-
const count = await this.runtime.countMemories(roomId, false, "messages");
|
|
809
|
-
return count >= this.memoryConfig.shortTermSummarizationThreshold;
|
|
810
|
-
}
|
|
811
|
-
getExtractionKey(entityId, roomId) {
|
|
812
|
-
return `memory:extraction:${entityId}:${roomId}`;
|
|
813
|
-
}
|
|
814
|
-
async getLastExtractionCheckpoint(entityId, roomId) {
|
|
815
|
-
const key = this.getExtractionKey(entityId, roomId);
|
|
816
|
-
const cached = this.lastExtractionCheckpoints.get(key);
|
|
817
|
-
if (cached !== undefined) {
|
|
818
|
-
return cached;
|
|
819
|
-
}
|
|
820
|
-
try {
|
|
821
|
-
const checkpoint = await this.runtime.getCache(key);
|
|
822
|
-
const messageCount = checkpoint ?? 0;
|
|
823
|
-
this.lastExtractionCheckpoints.set(key, messageCount);
|
|
824
|
-
return messageCount;
|
|
825
|
-
} catch (error) {
|
|
826
|
-
logger5.warn({ error }, "Failed to get extraction checkpoint from cache");
|
|
827
|
-
return 0;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
async setLastExtractionCheckpoint(entityId, roomId, messageCount) {
|
|
831
|
-
const key = this.getExtractionKey(entityId, roomId);
|
|
832
|
-
this.lastExtractionCheckpoints.set(key, messageCount);
|
|
833
|
-
try {
|
|
834
|
-
await this.runtime.setCache(key, messageCount);
|
|
835
|
-
logger5.debug(`Set extraction checkpoint for ${entityId} in room ${roomId} at count ${messageCount}`);
|
|
836
|
-
} catch (error) {
|
|
837
|
-
logger5.error({ error }, "Failed to persist extraction checkpoint to cache");
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
async shouldRunExtraction(entityId, roomId, currentMessageCount) {
|
|
841
|
-
const threshold = this.memoryConfig.longTermExtractionThreshold;
|
|
842
|
-
const interval = this.memoryConfig.longTermExtractionInterval;
|
|
843
|
-
if (currentMessageCount < threshold) {
|
|
844
|
-
return false;
|
|
845
|
-
}
|
|
846
|
-
const lastCheckpoint = await this.getLastExtractionCheckpoint(entityId, roomId);
|
|
847
|
-
const currentCheckpoint = Math.floor(currentMessageCount / interval) * interval;
|
|
848
|
-
const shouldRun = currentMessageCount >= threshold && currentCheckpoint > lastCheckpoint;
|
|
849
|
-
logger5.debug({
|
|
850
|
-
entityId,
|
|
851
|
-
roomId,
|
|
852
|
-
currentMessageCount,
|
|
853
|
-
threshold,
|
|
854
|
-
interval,
|
|
855
|
-
lastCheckpoint,
|
|
856
|
-
currentCheckpoint,
|
|
857
|
-
shouldRun
|
|
858
|
-
}, "Extraction check");
|
|
859
|
-
return shouldRun;
|
|
860
|
-
}
|
|
861
|
-
async storeLongTermMemory(memory) {
|
|
862
|
-
const db = this.getDb();
|
|
863
|
-
const id = crypto.randomUUID();
|
|
864
|
-
const now = new Date;
|
|
865
|
-
const newMemory = {
|
|
866
|
-
id,
|
|
867
|
-
createdAt: now,
|
|
868
|
-
updatedAt: now,
|
|
869
|
-
accessCount: 0,
|
|
870
|
-
...memory
|
|
871
|
-
};
|
|
872
|
-
try {
|
|
873
|
-
await db.insert(longTermMemories).values({
|
|
874
|
-
id: newMemory.id,
|
|
875
|
-
agentId: newMemory.agentId,
|
|
876
|
-
entityId: newMemory.entityId,
|
|
877
|
-
category: newMemory.category,
|
|
878
|
-
content: newMemory.content,
|
|
879
|
-
metadata: newMemory.metadata || {},
|
|
880
|
-
embedding: newMemory.embedding,
|
|
881
|
-
confidence: newMemory.confidence,
|
|
882
|
-
source: newMemory.source,
|
|
883
|
-
accessCount: newMemory.accessCount,
|
|
884
|
-
createdAt: now,
|
|
885
|
-
updatedAt: now,
|
|
886
|
-
lastAccessedAt: newMemory.lastAccessedAt
|
|
887
|
-
});
|
|
888
|
-
} catch (error) {
|
|
889
|
-
logger5.error({ error }, "Failed to store long-term memory");
|
|
890
|
-
throw error;
|
|
891
|
-
}
|
|
892
|
-
logger5.info(`Stored long-term memory: ${newMemory.category} for entity ${newMemory.entityId}`);
|
|
893
|
-
return newMemory;
|
|
894
|
-
}
|
|
895
|
-
async getLongTermMemories(entityId, category, limit = 10) {
|
|
896
|
-
const db = this.getDb();
|
|
897
|
-
const conditions = [
|
|
898
|
-
eq(longTermMemories.agentId, this.runtime.agentId),
|
|
899
|
-
eq(longTermMemories.entityId, entityId)
|
|
900
|
-
];
|
|
901
|
-
if (category) {
|
|
902
|
-
conditions.push(eq(longTermMemories.category, category));
|
|
903
|
-
}
|
|
904
|
-
const results = await db.select().from(longTermMemories).where(and(...conditions)).orderBy(desc(longTermMemories.confidence), desc(longTermMemories.updatedAt)).limit(limit);
|
|
905
|
-
return results.map((row) => ({
|
|
906
|
-
id: row.id,
|
|
907
|
-
agentId: row.agentId,
|
|
908
|
-
entityId: row.entityId,
|
|
909
|
-
category: row.category,
|
|
910
|
-
content: row.content,
|
|
911
|
-
metadata: row.metadata,
|
|
912
|
-
embedding: row.embedding,
|
|
913
|
-
confidence: row.confidence,
|
|
914
|
-
source: row.source,
|
|
915
|
-
createdAt: row.createdAt,
|
|
916
|
-
updatedAt: row.updatedAt,
|
|
917
|
-
lastAccessedAt: row.lastAccessedAt,
|
|
918
|
-
accessCount: row.accessCount
|
|
919
|
-
}));
|
|
920
|
-
}
|
|
921
|
-
async updateLongTermMemory(id, entityId, updates) {
|
|
922
|
-
const db = this.getDb();
|
|
923
|
-
const updateData = {
|
|
924
|
-
updatedAt: new Date
|
|
925
|
-
};
|
|
926
|
-
if (updates.content !== undefined)
|
|
927
|
-
updateData.content = updates.content;
|
|
928
|
-
if (updates.metadata !== undefined)
|
|
929
|
-
updateData.metadata = updates.metadata;
|
|
930
|
-
if (updates.confidence !== undefined)
|
|
931
|
-
updateData.confidence = updates.confidence;
|
|
932
|
-
if (updates.embedding !== undefined)
|
|
933
|
-
updateData.embedding = updates.embedding;
|
|
934
|
-
if (updates.lastAccessedAt !== undefined)
|
|
935
|
-
updateData.lastAccessedAt = updates.lastAccessedAt;
|
|
936
|
-
if (updates.accessCount !== undefined)
|
|
937
|
-
updateData.accessCount = updates.accessCount;
|
|
938
|
-
await db.update(longTermMemories).set(updateData).where(and(eq(longTermMemories.id, id), eq(longTermMemories.agentId, this.runtime.agentId), eq(longTermMemories.entityId, entityId)));
|
|
939
|
-
logger5.info(`Updated long-term memory: ${id} for entity ${entityId}`);
|
|
940
|
-
}
|
|
941
|
-
async deleteLongTermMemory(id, entityId) {
|
|
942
|
-
const db = this.getDb();
|
|
943
|
-
await db.delete(longTermMemories).where(and(eq(longTermMemories.id, id), eq(longTermMemories.agentId, this.runtime.agentId), eq(longTermMemories.entityId, entityId)));
|
|
944
|
-
logger5.info(`Deleted long-term memory: ${id} for entity ${entityId}`);
|
|
945
|
-
}
|
|
946
|
-
async getCurrentSessionSummary(roomId) {
|
|
947
|
-
const db = this.getDb();
|
|
948
|
-
const results = await db.select().from(sessionSummaries).where(and(eq(sessionSummaries.agentId, this.runtime.agentId), eq(sessionSummaries.roomId, roomId))).orderBy(desc(sessionSummaries.updatedAt)).limit(1);
|
|
949
|
-
if (results.length === 0) {
|
|
950
|
-
return null;
|
|
951
|
-
}
|
|
952
|
-
const row = results[0];
|
|
953
|
-
return {
|
|
954
|
-
id: row.id,
|
|
955
|
-
agentId: row.agentId,
|
|
956
|
-
roomId: row.roomId,
|
|
957
|
-
entityId: row.entityId,
|
|
958
|
-
summary: row.summary,
|
|
959
|
-
messageCount: row.messageCount,
|
|
960
|
-
lastMessageOffset: row.lastMessageOffset,
|
|
961
|
-
startTime: row.startTime,
|
|
962
|
-
endTime: row.endTime,
|
|
963
|
-
topics: row.topics || [],
|
|
964
|
-
metadata: row.metadata,
|
|
965
|
-
embedding: row.embedding,
|
|
966
|
-
createdAt: row.createdAt,
|
|
967
|
-
updatedAt: row.updatedAt
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
async storeSessionSummary(summary) {
|
|
971
|
-
const db = this.getDb();
|
|
972
|
-
const id = crypto.randomUUID();
|
|
973
|
-
const now = new Date;
|
|
974
|
-
const newSummary = {
|
|
975
|
-
id,
|
|
976
|
-
createdAt: now,
|
|
977
|
-
updatedAt: now,
|
|
978
|
-
...summary
|
|
979
|
-
};
|
|
980
|
-
await db.insert(sessionSummaries).values({
|
|
981
|
-
id: newSummary.id,
|
|
982
|
-
agentId: newSummary.agentId,
|
|
983
|
-
roomId: newSummary.roomId,
|
|
984
|
-
entityId: newSummary.entityId || null,
|
|
985
|
-
summary: newSummary.summary,
|
|
986
|
-
messageCount: newSummary.messageCount,
|
|
987
|
-
lastMessageOffset: newSummary.lastMessageOffset,
|
|
988
|
-
startTime: newSummary.startTime,
|
|
989
|
-
endTime: newSummary.endTime,
|
|
990
|
-
topics: newSummary.topics || [],
|
|
991
|
-
metadata: newSummary.metadata || {},
|
|
992
|
-
embedding: newSummary.embedding,
|
|
993
|
-
createdAt: now,
|
|
994
|
-
updatedAt: now
|
|
995
|
-
});
|
|
996
|
-
logger5.info(`Stored session summary for room ${newSummary.roomId}`);
|
|
997
|
-
return newSummary;
|
|
998
|
-
}
|
|
999
|
-
async updateSessionSummary(id, roomId, updates) {
|
|
1000
|
-
const db = this.getDb();
|
|
1001
|
-
const updateData = {
|
|
1002
|
-
updatedAt: new Date
|
|
1003
|
-
};
|
|
1004
|
-
if (updates.summary !== undefined)
|
|
1005
|
-
updateData.summary = updates.summary;
|
|
1006
|
-
if (updates.messageCount !== undefined)
|
|
1007
|
-
updateData.messageCount = updates.messageCount;
|
|
1008
|
-
if (updates.lastMessageOffset !== undefined)
|
|
1009
|
-
updateData.lastMessageOffset = updates.lastMessageOffset;
|
|
1010
|
-
if (updates.endTime !== undefined)
|
|
1011
|
-
updateData.endTime = updates.endTime;
|
|
1012
|
-
if (updates.topics !== undefined)
|
|
1013
|
-
updateData.topics = updates.topics;
|
|
1014
|
-
if (updates.metadata !== undefined)
|
|
1015
|
-
updateData.metadata = updates.metadata;
|
|
1016
|
-
if (updates.embedding !== undefined)
|
|
1017
|
-
updateData.embedding = updates.embedding;
|
|
1018
|
-
await db.update(sessionSummaries).set(updateData).where(and(eq(sessionSummaries.id, id), eq(sessionSummaries.agentId, this.runtime.agentId), eq(sessionSummaries.roomId, roomId)));
|
|
1019
|
-
logger5.info(`Updated session summary: ${id} for room ${roomId}`);
|
|
1020
|
-
}
|
|
1021
|
-
async getSessionSummaries(roomId, limit = 5) {
|
|
1022
|
-
const db = this.getDb();
|
|
1023
|
-
const results = await db.select().from(sessionSummaries).where(and(eq(sessionSummaries.agentId, this.runtime.agentId), eq(sessionSummaries.roomId, roomId))).orderBy(desc(sessionSummaries.updatedAt)).limit(limit);
|
|
1024
|
-
return results.map((row) => ({
|
|
1025
|
-
id: row.id,
|
|
1026
|
-
agentId: row.agentId,
|
|
1027
|
-
roomId: row.roomId,
|
|
1028
|
-
entityId: row.entityId,
|
|
1029
|
-
summary: row.summary,
|
|
1030
|
-
messageCount: row.messageCount,
|
|
1031
|
-
lastMessageOffset: row.lastMessageOffset,
|
|
1032
|
-
startTime: row.startTime,
|
|
1033
|
-
endTime: row.endTime,
|
|
1034
|
-
topics: row.topics || [],
|
|
1035
|
-
metadata: row.metadata,
|
|
1036
|
-
embedding: row.embedding,
|
|
1037
|
-
createdAt: row.createdAt,
|
|
1038
|
-
updatedAt: row.updatedAt
|
|
1039
|
-
}));
|
|
1040
|
-
}
|
|
1041
|
-
async searchLongTermMemories(entityId, queryEmbedding, limit = 5, matchThreshold = 0.7) {
|
|
1042
|
-
if (!this.memoryConfig.longTermVectorSearchEnabled) {
|
|
1043
|
-
logger5.warn("Vector search is not enabled, falling back to recent memories");
|
|
1044
|
-
return this.getLongTermMemories(entityId, undefined, limit);
|
|
1045
|
-
}
|
|
1046
|
-
const db = this.getDb();
|
|
420
|
+
// src/providers/memoryContext.ts
|
|
421
|
+
var memoryContextProvider = {
|
|
422
|
+
name: "MEMORY_CONTEXT",
|
|
423
|
+
description: "Provides relevant long-term memories from conversation context",
|
|
424
|
+
get: async (runtime, message, _state) => {
|
|
1047
425
|
try {
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
eq(longTermMemories.agentId, this.runtime.agentId),
|
|
1052
|
-
eq(longTermMemories.entityId, entityId),
|
|
1053
|
-
sql4`${longTermMemories.embedding} IS NOT NULL`
|
|
1054
|
-
];
|
|
1055
|
-
if (matchThreshold > 0) {
|
|
1056
|
-
conditions.push(gte(similarity, matchThreshold));
|
|
426
|
+
const memoryManager = runtime.getMemoryManager();
|
|
427
|
+
if (!memoryManager) {
|
|
428
|
+
return { text: "Memory manager is not available" };
|
|
1057
429
|
}
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
})
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
entityId: row.memory.entityId,
|
|
1066
|
-
category: row.memory.category,
|
|
1067
|
-
content: row.memory.content,
|
|
1068
|
-
metadata: row.memory.metadata ?? {},
|
|
1069
|
-
embedding: row.memory.embedding ?? [],
|
|
1070
|
-
confidence: row.memory.confidence ?? 1,
|
|
1071
|
-
source: row.memory.source ?? "",
|
|
1072
|
-
createdAt: row.memory.createdAt,
|
|
1073
|
-
updatedAt: row.memory.updatedAt,
|
|
1074
|
-
lastAccessedAt: row.memory.lastAccessedAt ?? row.memory.updatedAt,
|
|
1075
|
-
accessCount: row.memory.accessCount ?? 0,
|
|
1076
|
-
similarity: row.similarity
|
|
1077
|
-
}));
|
|
1078
|
-
} catch (error) {
|
|
1079
|
-
logger5.warn({ error }, "Vector search failed, falling back to recent memories");
|
|
1080
|
-
return this.getLongTermMemories(entityId, undefined, limit);
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
async getFormattedLongTermMemories(entityId) {
|
|
1084
|
-
const memories = await this.getLongTermMemories(entityId, undefined, 20);
|
|
1085
|
-
if (memories.length === 0) {
|
|
1086
|
-
return "";
|
|
1087
|
-
}
|
|
1088
|
-
const grouped = new Map;
|
|
1089
|
-
for (const memory of memories) {
|
|
1090
|
-
if (!grouped.has(memory.category)) {
|
|
1091
|
-
grouped.set(memory.category, []);
|
|
430
|
+
const memories = await memoryManager.getMemories({
|
|
431
|
+
roomId: message.roomId,
|
|
432
|
+
count: 50
|
|
433
|
+
});
|
|
434
|
+
const pluginMemories = memories.filter((m) => m.content.source === MEMORY_SOURCE);
|
|
435
|
+
if (pluginMemories.length === 0) {
|
|
436
|
+
return { text: "No stored memories available" };
|
|
1092
437
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
438
|
+
const parsedMemories = pluginMemories.map((m) => {
|
|
439
|
+
const parsed = decodeMemoryText(m.content.text);
|
|
440
|
+
return {
|
|
441
|
+
id: m.id ?? "",
|
|
442
|
+
content: parsed.content,
|
|
443
|
+
tags: parsed.tags,
|
|
444
|
+
importance: parsed.importance,
|
|
445
|
+
createdAt: m.createdAt ?? 0
|
|
446
|
+
};
|
|
447
|
+
}).sort((a, b) => {
|
|
448
|
+
if (a.importance !== b.importance)
|
|
449
|
+
return b.importance - a.importance;
|
|
450
|
+
return b.createdAt - a.createdAt;
|
|
451
|
+
}).slice(0, 20);
|
|
452
|
+
const memoryList = parsedMemories.map((m) => {
|
|
453
|
+
const tagStr = m.tags.length > 0 ? ` [${m.tags.join(", ")}]` : "";
|
|
454
|
+
const level = IMPORTANCE_LABELS[m.importance] ?? "normal";
|
|
455
|
+
return `- (${level}) ${m.content}${tagStr}`;
|
|
456
|
+
});
|
|
457
|
+
const text = `Stored Memories (${parsedMemories.length}):
|
|
458
|
+
${memoryList.join(`
|
|
459
|
+
`)}`;
|
|
460
|
+
return {
|
|
461
|
+
text,
|
|
462
|
+
data: {
|
|
463
|
+
memories: parsedMemories.map((m) => ({
|
|
464
|
+
id: m.id,
|
|
465
|
+
content: m.content,
|
|
466
|
+
tags: m.tags,
|
|
467
|
+
importance: m.importance
|
|
468
|
+
})),
|
|
469
|
+
count: parsedMemories.length
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
} catch (_error) {
|
|
473
|
+
return { text: "Error retrieving stored memories" };
|
|
1102
474
|
}
|
|
1103
|
-
return sections.join(`
|
|
1104
|
-
|
|
1105
|
-
`);
|
|
1106
475
|
}
|
|
1107
|
-
}
|
|
476
|
+
};
|
|
1108
477
|
|
|
1109
478
|
// src/index.ts
|
|
1110
479
|
var memoryPlugin = {
|
|
1111
|
-
name: "memory",
|
|
1112
|
-
description: "
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
providers: [longTermMemoryProvider, contextSummaryProvider],
|
|
1116
|
-
schema: exports_schemas
|
|
480
|
+
name: "@elizaos/plugin-memory-ts",
|
|
481
|
+
description: "Plugin for long-term memory management with remember, recall, and forget capabilities",
|
|
482
|
+
actions: [rememberAction, recallAction, forgetAction],
|
|
483
|
+
providers: [memoryContextProvider]
|
|
1117
484
|
};
|
|
1118
|
-
var src_default = memoryPlugin;
|
|
1119
485
|
export {
|
|
1120
|
-
|
|
1121
|
-
|
|
486
|
+
rememberAction,
|
|
487
|
+
recallAction,
|
|
1122
488
|
memoryPlugin,
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
489
|
+
memoryContextProvider,
|
|
490
|
+
forgetAction,
|
|
491
|
+
encodeMemoryText,
|
|
492
|
+
decodeMemoryText,
|
|
493
|
+
MemoryImportance,
|
|
494
|
+
MEMORY_SOURCE,
|
|
495
|
+
MEMORY_METADATA_SEPARATOR,
|
|
496
|
+
IMPORTANCE_LABELS
|
|
1131
497
|
};
|
|
1132
498
|
|
|
1133
|
-
//# debugId=
|
|
499
|
+
//# debugId=C7F07CBEF581DD5B64756E2164756E21
|