@louloulinx/metagpt 0.1.3
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/.eslintrc.json +23 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README-CN.md +754 -0
- package/README.md +238 -0
- package/bun.lock +1023 -0
- package/doc/TutorialAssistant.md +114 -0
- package/doc/VercelLLMProvider.md +164 -0
- package/eslint.config.js +55 -0
- package/examples/data-interpreter-example.ts +173 -0
- package/examples/qwen-direct-example.ts +60 -0
- package/examples/qwen-example.ts +62 -0
- package/examples/tutorial-assistant-example.ts +97 -0
- package/jest.config.ts +22 -0
- package/output/tutorials/Go/350/257/255/350/250/200/347/274/226/347/250/213/346/225/231/347/250/213_2025-02-25T09-35-15-436Z.md +2208 -0
- package/output/tutorials/Rust/346/225/231/347/250/213_2025-02-25T08-27-27-632Z.md +1967 -0
- package/output/tutorials//345/246/202/344/275/225/344/275/277/347/224/250TypeScript/345/274/200/345/217/221Node.js/345/272/224/347/224/250_2025-02-25T08-14-39-605Z.md +1721 -0
- package/output/tutorials//346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/346/225/231/347/250/213_2025-02-25T10-45-03-605Z.md +902 -0
- package/output/tutorials//346/232/250/345/215/227/345/244/247/345/255/246/346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/345/244/215/350/257/225/350/265/204/346/226/231_2025-02-25T11-16-59-133Z.md +719 -0
- package/package.json +58 -0
- package/plan-cn.md +321 -0
- package/plan.md +154 -0
- package/src/actions/analyze-task.ts +65 -0
- package/src/actions/base-action.ts +103 -0
- package/src/actions/di/execute-nb-code.ts +247 -0
- package/src/actions/di/write-analysis-code.ts +234 -0
- package/src/actions/write-tutorial.ts +232 -0
- package/src/config/browser.ts +33 -0
- package/src/config/config.ts +345 -0
- package/src/config/embedding.ts +26 -0
- package/src/config/llm.ts +36 -0
- package/src/config/mermaid.ts +37 -0
- package/src/config/omniparse.ts +25 -0
- package/src/config/redis.ts +34 -0
- package/src/config/s3.ts +33 -0
- package/src/config/search.ts +30 -0
- package/src/config/workspace.ts +20 -0
- package/src/index.ts +40 -0
- package/src/management/team.ts +168 -0
- package/src/memory/longterm.ts +218 -0
- package/src/memory/manager.ts +160 -0
- package/src/memory/types.ts +100 -0
- package/src/memory/working.ts +154 -0
- package/src/monitoring/system.ts +413 -0
- package/src/monitoring/types.ts +230 -0
- package/src/plugin/manager.ts +79 -0
- package/src/plugin/types.ts +114 -0
- package/src/provider/vercel-llm.ts +314 -0
- package/src/rag/base-rag.ts +194 -0
- package/src/rag/document-qa.ts +102 -0
- package/src/roles/base-role.ts +155 -0
- package/src/roles/data-interpreter.ts +360 -0
- package/src/roles/engineer.ts +1 -0
- package/src/roles/tutorial-assistant.ts +217 -0
- package/src/skills/base-skill.ts +144 -0
- package/src/skills/code-review.ts +120 -0
- package/src/tools/base-tool.ts +155 -0
- package/src/tools/file-system.ts +204 -0
- package/src/tools/tool-recommend.d.ts +14 -0
- package/src/tools/tool-recommend.ts +31 -0
- package/src/types/action.ts +38 -0
- package/src/types/config.ts +129 -0
- package/src/types/document.ts +354 -0
- package/src/types/llm.ts +64 -0
- package/src/types/memory.ts +36 -0
- package/src/types/message.ts +193 -0
- package/src/types/rag.ts +86 -0
- package/src/types/role.ts +67 -0
- package/src/types/skill.ts +71 -0
- package/src/types/task.ts +32 -0
- package/src/types/team.ts +55 -0
- package/src/types/tool.ts +77 -0
- package/src/types/workflow.ts +133 -0
- package/src/utils/common.ts +73 -0
- package/src/utils/yaml.ts +67 -0
- package/src/websocket/browser-client.ts +187 -0
- package/src/websocket/client.ts +186 -0
- package/src/websocket/server.ts +169 -0
- package/src/websocket/types.ts +125 -0
- package/src/workflow/executor.ts +193 -0
- package/src/workflow/executors/action-executor.ts +72 -0
- package/src/workflow/executors/condition-executor.ts +118 -0
- package/src/workflow/executors/parallel-executor.ts +201 -0
- package/src/workflow/executors/role-executor.ts +76 -0
- package/src/workflow/executors/sequence-executor.ts +196 -0
- package/tests/actions.test.ts +105 -0
- package/tests/benchmark/performance.test.ts +147 -0
- package/tests/config/config.test.ts +115 -0
- package/tests/config.test.ts +106 -0
- package/tests/e2e/setup.ts +74 -0
- package/tests/e2e/workflow.test.ts +88 -0
- package/tests/llm.test.ts +84 -0
- package/tests/memory/memory.test.ts +164 -0
- package/tests/memory.test.ts +63 -0
- package/tests/monitoring/monitoring.test.ts +225 -0
- package/tests/plugin/plugin.test.ts +183 -0
- package/tests/provider/bailian-llm.test.ts +98 -0
- package/tests/rag.test.ts +162 -0
- package/tests/roles.test.ts +88 -0
- package/tests/skills.test.ts +166 -0
- package/tests/team.test.ts +143 -0
- package/tests/tools.test.ts +170 -0
- package/tests/types/document.test.ts +181 -0
- package/tests/types/message.test.ts +122 -0
- package/tests/utils/yaml.test.ts +110 -0
- package/tests/utils.test.ts +74 -0
- package/tests/websocket/browser-client.test.ts +1 -0
- package/tests/websocket/websocket.test.ts +42 -0
- package/tests/workflow/parallel-executor.test.ts +224 -0
- package/tests/workflow/sequence-executor.test.ts +207 -0
- package/tests/workflow.test.ts +290 -0
- package/tsconfig.json +27 -0
- package/typedoc.json +25 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import { Environment } from '../environment/environment';
|
3
|
+
import { Context } from '../context/context';
|
4
|
+
import { Message } from '../types/message';
|
5
|
+
import { Role } from '../types/role';
|
6
|
+
import { TeamConfig, TeamConfigSchema, TeamState, TeamStateSchema } from '../types/team';
|
7
|
+
import { NoMoneyError } from '../utils/errors';
|
8
|
+
import { readJsonFile, writeJsonFile } from '../utils/file';
|
9
|
+
import { join } from 'path';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Team class for managing multiple roles (agents) and their interactions
|
13
|
+
*/
|
14
|
+
export class Team {
|
15
|
+
private config: TeamConfig;
|
16
|
+
private state: TeamState;
|
17
|
+
private env: Environment;
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Create a new team instance
|
21
|
+
* @param config Team configuration
|
22
|
+
*/
|
23
|
+
constructor(config: TeamConfig) {
|
24
|
+
this.config = TeamConfigSchema.parse(config);
|
25
|
+
this.state = TeamStateSchema.parse({});
|
26
|
+
|
27
|
+
const context = config.context || new Context();
|
28
|
+
this.env = config.environment || new Environment({ context });
|
29
|
+
|
30
|
+
if (config.roles) {
|
31
|
+
this.hire(config.roles);
|
32
|
+
}
|
33
|
+
|
34
|
+
if (config.envDesc) {
|
35
|
+
this.env.setDescription(config.envDesc);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Hire roles to join the team
|
41
|
+
* @param roles List of roles to hire
|
42
|
+
*/
|
43
|
+
public hire(roles: Role[]): void {
|
44
|
+
this.env.addRoles(roles);
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Get the cost manager
|
49
|
+
*/
|
50
|
+
public get costManager() {
|
51
|
+
return this.env.context.costManager;
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Invest in the team
|
56
|
+
* @param amount Investment amount
|
57
|
+
*/
|
58
|
+
public invest(amount: number): void {
|
59
|
+
this.config.investment = amount;
|
60
|
+
this.costManager.maxBudget = amount;
|
61
|
+
console.log(`Investment: $${amount}`);
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Check if team has sufficient balance
|
66
|
+
* @throws {NoMoneyError} If insufficient funds
|
67
|
+
*/
|
68
|
+
private checkBalance(): void {
|
69
|
+
if (this.costManager.totalCost >= this.costManager.maxBudget) {
|
70
|
+
throw new NoMoneyError(
|
71
|
+
this.costManager.totalCost,
|
72
|
+
`Insufficient funds: ${this.costManager.maxBudget}`
|
73
|
+
);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Run a project with given idea
|
79
|
+
* @param idea Project idea or requirement
|
80
|
+
* @param sendTo Target role (optional)
|
81
|
+
*/
|
82
|
+
public runProject(idea: string, sendTo = ''): void {
|
83
|
+
this.config.idea = idea;
|
84
|
+
|
85
|
+
// Publish human requirement
|
86
|
+
this.env.publishMessage(
|
87
|
+
new Message({
|
88
|
+
role: 'Human',
|
89
|
+
content: idea,
|
90
|
+
causedBy: 'UserRequirement',
|
91
|
+
sendTo: sendTo || 'ALL'
|
92
|
+
})
|
93
|
+
);
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Run the team for specified rounds
|
98
|
+
* @param rounds Number of rounds to run
|
99
|
+
* @param idea Optional project idea
|
100
|
+
* @param sendTo Optional target role
|
101
|
+
* @param autoArchive Whether to auto archive after completion
|
102
|
+
*/
|
103
|
+
public async run(
|
104
|
+
rounds = 3,
|
105
|
+
idea = '',
|
106
|
+
sendTo = '',
|
107
|
+
autoArchive = true
|
108
|
+
): Promise<Message[]> {
|
109
|
+
if (idea) {
|
110
|
+
this.runProject(idea, sendTo);
|
111
|
+
}
|
112
|
+
|
113
|
+
while (rounds > 0) {
|
114
|
+
if (this.env.isIdle) {
|
115
|
+
console.debug('All roles are idle.');
|
116
|
+
break;
|
117
|
+
}
|
118
|
+
|
119
|
+
rounds--;
|
120
|
+
this.checkBalance();
|
121
|
+
await this.env.run();
|
122
|
+
|
123
|
+
console.debug(`Max ${rounds} rounds left.`);
|
124
|
+
}
|
125
|
+
|
126
|
+
if (autoArchive) {
|
127
|
+
await this.env.archive();
|
128
|
+
}
|
129
|
+
|
130
|
+
return this.env.history;
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Serialize team state to storage
|
135
|
+
* @param storagePath Storage path
|
136
|
+
*/
|
137
|
+
public serialize(storagePath: string): void {
|
138
|
+
const teamPath = join(storagePath, 'team');
|
139
|
+
const teamInfoPath = join(teamPath, 'team.json');
|
140
|
+
|
141
|
+
const data = {
|
142
|
+
config: this.config,
|
143
|
+
state: this.state,
|
144
|
+
context: this.env.context.serialize()
|
145
|
+
};
|
146
|
+
|
147
|
+
writeJsonFile(teamInfoPath, data);
|
148
|
+
}
|
149
|
+
|
150
|
+
/**
|
151
|
+
* Deserialize team state from storage
|
152
|
+
* @param storagePath Storage path
|
153
|
+
* @param context Optional context
|
154
|
+
*/
|
155
|
+
public static deserialize(storagePath: string, context?: Context): Team {
|
156
|
+
const teamPath = join(storagePath, 'team');
|
157
|
+
const teamInfoPath = join(teamPath, 'team.json');
|
158
|
+
|
159
|
+
const data = readJsonFile(teamInfoPath);
|
160
|
+
const ctx = context || new Context();
|
161
|
+
ctx.deserialize(data.context);
|
162
|
+
|
163
|
+
return new Team({
|
164
|
+
...data.config,
|
165
|
+
context: ctx
|
166
|
+
});
|
167
|
+
}
|
168
|
+
}
|
@@ -0,0 +1,218 @@
|
|
1
|
+
import type { LongTermMemory, WorkingMemory } from './types';
|
2
|
+
import type { MemoryEntrySchema, MemoryQueryOptions } from './types';
|
3
|
+
import type { z } from 'zod';
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Long-term memory implementation with persistence and consolidation
|
8
|
+
*/
|
9
|
+
export class LongTermMemoryImpl implements LongTermMemory {
|
10
|
+
private memories: Map<string, z.infer<typeof MemoryEntrySchema>> = new Map();
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Add a new memory
|
14
|
+
*/
|
15
|
+
public async add(
|
16
|
+
content: string,
|
17
|
+
type: string,
|
18
|
+
metadata: Record<string, any> = {}
|
19
|
+
): Promise<z.infer<typeof MemoryEntrySchema>> {
|
20
|
+
const memory = {
|
21
|
+
id: uuidv4(),
|
22
|
+
content,
|
23
|
+
type,
|
24
|
+
timestamp: Date.now(),
|
25
|
+
metadata,
|
26
|
+
importance: metadata.importance ?? 0.5,
|
27
|
+
embedding: metadata.embedding ?? [],
|
28
|
+
};
|
29
|
+
|
30
|
+
this.memories.set(memory.id, memory);
|
31
|
+
return memory;
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Get a memory by ID
|
36
|
+
*/
|
37
|
+
public async get(id: string): Promise<z.infer<typeof MemoryEntrySchema> | null> {
|
38
|
+
return this.memories.get(id) || null;
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Search memories by query options
|
43
|
+
*/
|
44
|
+
public async search(options: MemoryQueryOptions): Promise<z.infer<typeof MemoryEntrySchema>[]> {
|
45
|
+
let results = Array.from(this.memories.values());
|
46
|
+
|
47
|
+
// Apply filters
|
48
|
+
if (options.type) {
|
49
|
+
results = results.filter(m => m.type === options.type);
|
50
|
+
}
|
51
|
+
|
52
|
+
if (options.startTime) {
|
53
|
+
results = results.filter(m => m.timestamp >= options.startTime!);
|
54
|
+
}
|
55
|
+
|
56
|
+
if (options.endTime) {
|
57
|
+
results = results.filter(m => m.timestamp <= options.endTime!);
|
58
|
+
}
|
59
|
+
|
60
|
+
if (options.minImportance) {
|
61
|
+
results = results.filter(m => m.importance >= options.minImportance!);
|
62
|
+
}
|
63
|
+
|
64
|
+
if (options.metadata) {
|
65
|
+
results = results.filter(m => {
|
66
|
+
return Object.entries(options.metadata!).every(([key, value]) =>
|
67
|
+
m.metadata[key] === value
|
68
|
+
);
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
if (options.content) {
|
73
|
+
const searchContent = options.content.toLowerCase();
|
74
|
+
results = results.filter(m =>
|
75
|
+
m.content.toLowerCase().includes(searchContent) ||
|
76
|
+
searchContent.includes(m.content.toLowerCase())
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
// Sort by importance and timestamp
|
81
|
+
results.sort((a, b) => {
|
82
|
+
const importanceDiff = b.importance - a.importance;
|
83
|
+
return importanceDiff !== 0 ? importanceDiff : b.timestamp - a.timestamp;
|
84
|
+
});
|
85
|
+
|
86
|
+
// Apply limit
|
87
|
+
if (options.limit) {
|
88
|
+
results = results.slice(0, options.limit);
|
89
|
+
}
|
90
|
+
|
91
|
+
return results;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Update a memory
|
96
|
+
*/
|
97
|
+
public async update(
|
98
|
+
id: string,
|
99
|
+
updates: Partial<z.infer<typeof MemoryEntrySchema>>
|
100
|
+
): Promise<void> {
|
101
|
+
const memory = this.memories.get(id);
|
102
|
+
if (!memory) {
|
103
|
+
throw new Error(`Memory ${id} not found`);
|
104
|
+
}
|
105
|
+
|
106
|
+
Object.assign(memory, updates);
|
107
|
+
this.memories.set(id, memory);
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Delete a memory
|
112
|
+
*/
|
113
|
+
public async delete(id: string): Promise<void> {
|
114
|
+
this.memories.delete(id);
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Clear all memories
|
119
|
+
*/
|
120
|
+
public async clear(): Promise<void> {
|
121
|
+
this.memories.clear();
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Consolidate memories from working memory
|
126
|
+
* This process involves:
|
127
|
+
* 1. Evaluating importance of working memories
|
128
|
+
* 2. Filtering out low importance memories
|
129
|
+
* 3. Generating embeddings for semantic search
|
130
|
+
* 4. Storing consolidated memories
|
131
|
+
*/
|
132
|
+
public async consolidate(workingMemory: WorkingMemory): Promise<void> {
|
133
|
+
// Get all memories from working memory
|
134
|
+
const memories = await workingMemory.search({});
|
135
|
+
|
136
|
+
for (const memory of memories) {
|
137
|
+
// Skip if already consolidated
|
138
|
+
if (this.memories.has(memory.id)) {
|
139
|
+
continue;
|
140
|
+
}
|
141
|
+
|
142
|
+
// Check for similar existing memories
|
143
|
+
const similarMemories = await this.search({
|
144
|
+
content: memory.content,
|
145
|
+
limit: 1,
|
146
|
+
});
|
147
|
+
|
148
|
+
// If a very similar memory exists, update its importance instead of creating new
|
149
|
+
if (similarMemories.length > 0 &&
|
150
|
+
(similarMemories[0].content === memory.content ||
|
151
|
+
similarMemories[0].content.includes(memory.content) ||
|
152
|
+
memory.content.includes(similarMemories[0].content))) {
|
153
|
+
const existingMemory = similarMemories[0];
|
154
|
+
await this.update(existingMemory.id, {
|
155
|
+
importance: Math.max(existingMemory.importance, memory.importance),
|
156
|
+
metadata: {
|
157
|
+
...existingMemory.metadata,
|
158
|
+
lastReinforced: Date.now(),
|
159
|
+
},
|
160
|
+
});
|
161
|
+
continue;
|
162
|
+
}
|
163
|
+
|
164
|
+
// Evaluate importance based on:
|
165
|
+
// - Explicit importance score
|
166
|
+
// - Age of memory
|
167
|
+
// - Number of related memories
|
168
|
+
const age = Date.now() - memory.timestamp;
|
169
|
+
const relatedMemories = await this.search({
|
170
|
+
content: memory.content,
|
171
|
+
limit: 5,
|
172
|
+
});
|
173
|
+
|
174
|
+
const importance = Math.min(
|
175
|
+
1,
|
176
|
+
memory.importance * 0.7 + // Base importance has higher weight
|
177
|
+
(relatedMemories.length * 0.1) - // Related memories boost importance
|
178
|
+
(age / (24 * 60 * 60 * 1000) * 0.2) // Age reduces importance more significantly
|
179
|
+
);
|
180
|
+
|
181
|
+
// Add new memory with updated importance
|
182
|
+
await this.add(memory.content, memory.type, {
|
183
|
+
...memory.metadata,
|
184
|
+
importance,
|
185
|
+
originalId: memory.id,
|
186
|
+
consolidatedAt: Date.now(),
|
187
|
+
});
|
188
|
+
}
|
189
|
+
|
190
|
+
// Clear consolidated memories from working memory
|
191
|
+
await workingMemory.clear();
|
192
|
+
}
|
193
|
+
|
194
|
+
/**
|
195
|
+
* Forget old or unimportant memories
|
196
|
+
*/
|
197
|
+
public async forget(options: MemoryQueryOptions): Promise<void> {
|
198
|
+
const memories = Array.from(this.memories.values());
|
199
|
+
|
200
|
+
// Find memories to forget based on time and importance
|
201
|
+
const toForget = memories.filter(memory => {
|
202
|
+
// Check if memory is old enough using metadata timestamp if available
|
203
|
+
const timestamp = memory.metadata.timestamp ?? memory.timestamp;
|
204
|
+
const isOld = options.endTime ? timestamp <= options.endTime : false;
|
205
|
+
|
206
|
+
// Check if memory is not important enough
|
207
|
+
const isUnimportant = options.minImportance ? memory.importance < options.minImportance : false;
|
208
|
+
|
209
|
+
// Only forget memories that are both old and unimportant
|
210
|
+
return isOld && isUnimportant;
|
211
|
+
});
|
212
|
+
|
213
|
+
// Delete forgotten memories
|
214
|
+
for (const memory of toForget) {
|
215
|
+
await this.delete(memory.id);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import type { MemoryManager } from './types';
|
2
|
+
import type { Message } from '../types/message';
|
3
|
+
import type { z } from 'zod';
|
4
|
+
import type { MemoryEntrySchema } from './types';
|
5
|
+
import { WorkingMemoryImpl } from './working';
|
6
|
+
import { LongTermMemoryImpl } from './longterm';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Memory manager implementation for coordinating working and long-term memory
|
10
|
+
*/
|
11
|
+
export class MemoryManagerImpl implements MemoryManager {
|
12
|
+
public readonly working: WorkingMemoryImpl;
|
13
|
+
public readonly longTerm: LongTermMemoryImpl;
|
14
|
+
|
15
|
+
constructor() {
|
16
|
+
this.working = new WorkingMemoryImpl();
|
17
|
+
this.longTerm = new LongTermMemoryImpl();
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Initialize memory systems
|
22
|
+
*/
|
23
|
+
public async init(): Promise<void> {
|
24
|
+
// Initialize working memory
|
25
|
+
await this.working.clear();
|
26
|
+
|
27
|
+
// Initialize long-term memory
|
28
|
+
await this.longTerm.clear();
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Process a message through memory systems
|
33
|
+
* This involves:
|
34
|
+
* 1. Storing the message in working memory
|
35
|
+
* 2. Updating importance based on context
|
36
|
+
* 3. Triggering consolidation if needed
|
37
|
+
*/
|
38
|
+
public async processMessage(message: Message): Promise<void> {
|
39
|
+
// Store message in working memory
|
40
|
+
const memory = await this.working.add(message.content, 'message', {
|
41
|
+
role: message.role,
|
42
|
+
causedBy: message.causedBy,
|
43
|
+
sentFrom: message.sentFrom,
|
44
|
+
sendTo: Array.from(message.sendTo),
|
45
|
+
instructContent: message.instructContent,
|
46
|
+
timestamp: Date.now(),
|
47
|
+
});
|
48
|
+
|
49
|
+
// Update importance based on:
|
50
|
+
// - Message role (system > assistant > user)
|
51
|
+
// - Presence of instruction content
|
52
|
+
// - Number of recipients
|
53
|
+
let importance = 0.5;
|
54
|
+
|
55
|
+
if (message.role === 'system') {
|
56
|
+
importance += 0.3;
|
57
|
+
} else if (message.role === 'assistant') {
|
58
|
+
importance += 0.2;
|
59
|
+
}
|
60
|
+
|
61
|
+
if (message.instructContent) {
|
62
|
+
importance += 0.1;
|
63
|
+
}
|
64
|
+
|
65
|
+
if (message.sendTo.size > 1) {
|
66
|
+
importance += 0.1;
|
67
|
+
}
|
68
|
+
|
69
|
+
await this.working.update(memory.id, { importance });
|
70
|
+
|
71
|
+
// Get working memory size
|
72
|
+
const workingMemories = await this.working.search({});
|
73
|
+
|
74
|
+
// Trigger consolidation if working memory is too large
|
75
|
+
if (workingMemories.length > 100) {
|
76
|
+
await this.longTerm.consolidate(this.working);
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Retrieve relevant memories for context
|
82
|
+
*/
|
83
|
+
public async getContext(message: Message): Promise<z.infer<typeof MemoryEntrySchema>[]> {
|
84
|
+
// Get recent memories from working memory (last 1 hour)
|
85
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
86
|
+
const workingMemories = await this.working.search({
|
87
|
+
startTime: oneHourAgo,
|
88
|
+
limit: 10,
|
89
|
+
});
|
90
|
+
|
91
|
+
// Get relevant memories from long-term memory using content search
|
92
|
+
const searchTerms = message.content.toLowerCase().split(/\s+/);
|
93
|
+
const longTermMemories = await this.longTerm.search({
|
94
|
+
content: message.content,
|
95
|
+
minImportance: 0.2,
|
96
|
+
limit: 20,
|
97
|
+
});
|
98
|
+
|
99
|
+
// Calculate relevance scores for all memories
|
100
|
+
const scoredMemories = [...workingMemories, ...longTermMemories].map(memory => {
|
101
|
+
// Time decay factor (exponential decay over 24 hours)
|
102
|
+
const timeDecay = Math.exp(-(Date.now() - memory.timestamp) / (24 * 60 * 60 * 1000));
|
103
|
+
|
104
|
+
// Content similarity (word overlap)
|
105
|
+
const memoryWords = memory.content.toLowerCase().split(/\s+/);
|
106
|
+
const overlap = searchTerms.filter(term =>
|
107
|
+
memoryWords.some(word => word.includes(term) || term.includes(word))
|
108
|
+
).length;
|
109
|
+
const contentSimilarity = overlap / Math.max(searchTerms.length, memoryWords.length);
|
110
|
+
|
111
|
+
// Combine scores with weights
|
112
|
+
const score = (timeDecay * 0.2) + // Recency
|
113
|
+
(memory.importance * 0.3) + // Importance
|
114
|
+
(contentSimilarity * 0.5); // Content matching (higher weight)
|
115
|
+
|
116
|
+
return { memory, score };
|
117
|
+
});
|
118
|
+
|
119
|
+
// Sort by score and return top memories
|
120
|
+
return scoredMemories
|
121
|
+
.sort((a, b) => b.score - a.score)
|
122
|
+
.slice(0, 5)
|
123
|
+
.map(item => item.memory);
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Calculate cosine similarity between two vectors
|
128
|
+
*/
|
129
|
+
private calculateCosineSimilarity(a: number[], b: number[]): number {
|
130
|
+
if (a.length !== b.length) return 0;
|
131
|
+
|
132
|
+
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
|
133
|
+
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
|
134
|
+
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
|
135
|
+
|
136
|
+
return magnitudeA && magnitudeB ? dotProduct / (magnitudeA * magnitudeB) : 0;
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* Cleanup memory systems
|
141
|
+
* This involves:
|
142
|
+
* 1. Consolidating remaining working memories
|
143
|
+
* 2. Forgetting old and unimportant memories
|
144
|
+
* 3. Clearing working memory
|
145
|
+
*/
|
146
|
+
public async cleanup(): Promise<void> {
|
147
|
+
// Consolidate remaining working memories
|
148
|
+
await this.longTerm.consolidate(this.working);
|
149
|
+
|
150
|
+
// Forget old memories
|
151
|
+
const oneMonthAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
152
|
+
await this.longTerm.forget({
|
153
|
+
endTime: oneMonthAgo,
|
154
|
+
minImportance: 0.8, // Keep very important old memories
|
155
|
+
});
|
156
|
+
|
157
|
+
// Clear working memory
|
158
|
+
await this.working.clear();
|
159
|
+
}
|
160
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import type { Message } from '../types/message';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Memory entry schema for storing individual memories
|
6
|
+
*/
|
7
|
+
export const MemoryEntrySchema = z.object({
|
8
|
+
/** Unique identifier for the memory */
|
9
|
+
id: z.string().uuid(),
|
10
|
+
/** Content of the memory */
|
11
|
+
content: z.string(),
|
12
|
+
/** Type of memory (e.g., conversation, fact, experience) */
|
13
|
+
type: z.string(),
|
14
|
+
/** Creation timestamp */
|
15
|
+
timestamp: z.number().default(() => Date.now()),
|
16
|
+
/** Associated metadata */
|
17
|
+
metadata: z.record(z.any()).default({}),
|
18
|
+
/** Importance score (0-1) */
|
19
|
+
importance: z.number().min(0).max(1).default(0.5),
|
20
|
+
/** Vector embedding for similarity search */
|
21
|
+
embedding: z.array(z.number()).optional(),
|
22
|
+
});
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Memory query options
|
26
|
+
*/
|
27
|
+
export interface MemoryQueryOptions {
|
28
|
+
/** Content to search for */
|
29
|
+
content?: string;
|
30
|
+
/** Type of memories to search */
|
31
|
+
type?: string;
|
32
|
+
/** Time range start */
|
33
|
+
startTime?: number;
|
34
|
+
/** Time range end */
|
35
|
+
endTime?: number;
|
36
|
+
/** Minimum importance score */
|
37
|
+
minImportance?: number;
|
38
|
+
/** Maximum number of results */
|
39
|
+
limit?: number;
|
40
|
+
/** Metadata filters */
|
41
|
+
metadata?: Record<string, any>;
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Memory interface for storing and retrieving memories
|
46
|
+
*/
|
47
|
+
export interface Memory {
|
48
|
+
/** Add a new memory */
|
49
|
+
add(content: string, type: string, metadata?: Record<string, any>): Promise<z.infer<typeof MemoryEntrySchema>>;
|
50
|
+
/** Get a memory by ID */
|
51
|
+
get(id: string): Promise<z.infer<typeof MemoryEntrySchema> | null>;
|
52
|
+
/** Search memories by query options */
|
53
|
+
search(options: MemoryQueryOptions): Promise<z.infer<typeof MemoryEntrySchema>[]>;
|
54
|
+
/** Update a memory */
|
55
|
+
update(id: string, updates: Partial<z.infer<typeof MemoryEntrySchema>>): Promise<void>;
|
56
|
+
/** Delete a memory */
|
57
|
+
delete(id: string): Promise<void>;
|
58
|
+
/** Clear all memories */
|
59
|
+
clear(): Promise<void>;
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Working memory interface for temporary storage
|
64
|
+
*/
|
65
|
+
export interface WorkingMemory extends Memory {
|
66
|
+
/** Get current focus of attention */
|
67
|
+
getFocus(): Promise<z.infer<typeof MemoryEntrySchema> | null>;
|
68
|
+
/** Set focus of attention */
|
69
|
+
setFocus(id: string): Promise<void>;
|
70
|
+
/** Clear focus of attention */
|
71
|
+
clearFocus(): Promise<void>;
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Long-term memory interface for persistent storage
|
76
|
+
*/
|
77
|
+
export interface LongTermMemory extends Memory {
|
78
|
+
/** Consolidate memories from working memory */
|
79
|
+
consolidate(workingMemory: WorkingMemory): Promise<void>;
|
80
|
+
/** Forget old or unimportant memories */
|
81
|
+
forget(options: MemoryQueryOptions): Promise<void>;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Memory manager interface for coordinating different memory types
|
86
|
+
*/
|
87
|
+
export interface MemoryManager {
|
88
|
+
/** Working memory instance */
|
89
|
+
working: WorkingMemory;
|
90
|
+
/** Long-term memory instance */
|
91
|
+
longTerm: LongTermMemory;
|
92
|
+
/** Initialize memory systems */
|
93
|
+
init(): Promise<void>;
|
94
|
+
/** Process a message through memory */
|
95
|
+
processMessage(message: Message): Promise<void>;
|
96
|
+
/** Retrieve relevant memories for context */
|
97
|
+
getContext(message: Message): Promise<z.infer<typeof MemoryEntrySchema>[]>;
|
98
|
+
/** Cleanup memory systems */
|
99
|
+
cleanup(): Promise<void>;
|
100
|
+
}
|