@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,354 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import path from 'path';
|
3
|
+
import fs from 'fs/promises';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Document status enum, similar to RFC/PEP
|
7
|
+
*/
|
8
|
+
export enum DocumentStatus {
|
9
|
+
DRAFT = 'draft',
|
10
|
+
UNDERREVIEW = 'underreview',
|
11
|
+
APPROVED = 'approved',
|
12
|
+
DONE = 'done',
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Base document schema
|
17
|
+
*/
|
18
|
+
export const DocumentSchema = z.object({
|
19
|
+
path: z.string().optional(),
|
20
|
+
name: z.string().default(''),
|
21
|
+
content: z.string().default(''),
|
22
|
+
author: z.string().default(''),
|
23
|
+
status: z.nativeEnum(DocumentStatus).default(DocumentStatus.DRAFT),
|
24
|
+
reviews: z.array(z.any()).default([]),
|
25
|
+
});
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Indexable document schema for vector databases or search engines
|
29
|
+
*/
|
30
|
+
export const IndexableDocumentSchema = DocumentSchema.extend({
|
31
|
+
data: z.union([z.array(z.any()), z.record(z.string(), z.any())]),
|
32
|
+
contentCol: z.string().default(''),
|
33
|
+
metaCol: z.string().default(''),
|
34
|
+
});
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Repository metadata schema
|
38
|
+
*/
|
39
|
+
export const RepoMetadataSchema = z.object({
|
40
|
+
name: z.string().default(''),
|
41
|
+
nDocs: z.number().default(0),
|
42
|
+
nChars: z.number().default(0),
|
43
|
+
symbols: z.array(z.any()).default([]),
|
44
|
+
});
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Repository schema
|
48
|
+
*/
|
49
|
+
export const RepoSchema = z.object({
|
50
|
+
name: z.string().default(''),
|
51
|
+
docs: z.record(z.string(), DocumentSchema).default({}),
|
52
|
+
codes: z.record(z.string(), DocumentSchema).default({}),
|
53
|
+
assets: z.record(z.string(), DocumentSchema).default({}),
|
54
|
+
path: z.string().optional(),
|
55
|
+
});
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Document types
|
59
|
+
*/
|
60
|
+
export type Document = z.infer<typeof DocumentSchema> & {
|
61
|
+
toPath?: (path?: string) => Promise<void>;
|
62
|
+
};
|
63
|
+
export type IndexableDocument = z.infer<typeof IndexableDocumentSchema>;
|
64
|
+
export type RepoMetadata = z.infer<typeof RepoMetadataSchema>;
|
65
|
+
export type Repo = z.infer<typeof RepoSchema>;
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Document class implementation
|
69
|
+
*/
|
70
|
+
export class DocumentImpl implements Document {
|
71
|
+
public path?: string;
|
72
|
+
public name: string = '';
|
73
|
+
public content: string = '';
|
74
|
+
public author: string = '';
|
75
|
+
public status: DocumentStatus = DocumentStatus.DRAFT;
|
76
|
+
public reviews: any[] = [];
|
77
|
+
|
78
|
+
constructor(data: Partial<Document> = {}) {
|
79
|
+
Object.assign(this, DocumentSchema.parse(data));
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Create a Document instance from a file path
|
84
|
+
*/
|
85
|
+
public static async fromPath(filePath: string): Promise<DocumentImpl> {
|
86
|
+
try {
|
87
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
88
|
+
return new DocumentImpl({
|
89
|
+
content,
|
90
|
+
path: filePath,
|
91
|
+
name: path.basename(filePath),
|
92
|
+
});
|
93
|
+
} catch (error: unknown) {
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
95
|
+
throw new Error(`Failed to read file ${filePath}: ${message}`);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Create a Document from a text string
|
101
|
+
*/
|
102
|
+
public static fromText(text: string, filePath?: string): DocumentImpl {
|
103
|
+
return new DocumentImpl({
|
104
|
+
content: text,
|
105
|
+
path: filePath,
|
106
|
+
name: filePath ? path.basename(filePath) : '',
|
107
|
+
});
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Save content to the specified file path
|
112
|
+
*/
|
113
|
+
public async toPath(filePath?: string): Promise<void> {
|
114
|
+
const targetPath = filePath || this.path;
|
115
|
+
if (!targetPath) {
|
116
|
+
throw new Error('File path is not set.');
|
117
|
+
}
|
118
|
+
|
119
|
+
try {
|
120
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
121
|
+
await fs.writeFile(targetPath, this.content, 'utf-8');
|
122
|
+
this.path = targetPath;
|
123
|
+
} catch (error: unknown) {
|
124
|
+
const message = error instanceof Error ? error.message : String(error);
|
125
|
+
throw new Error(`Failed to write file ${targetPath}: ${message}`);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Persist document to disk
|
131
|
+
*/
|
132
|
+
public async persist(): Promise<void> {
|
133
|
+
return this.toPath();
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Indexable document class implementation
|
139
|
+
*/
|
140
|
+
export class IndexableDocumentImpl extends DocumentImpl implements IndexableDocument {
|
141
|
+
public data: any[] | Record<string, any>;
|
142
|
+
public contentCol: string = '';
|
143
|
+
public metaCol: string = '';
|
144
|
+
|
145
|
+
constructor(data: Partial<IndexableDocument> = {}) {
|
146
|
+
super(data);
|
147
|
+
const parsed = IndexableDocumentSchema.parse(data);
|
148
|
+
this.data = parsed.data;
|
149
|
+
this.contentCol = parsed.contentCol;
|
150
|
+
this.metaCol = parsed.metaCol;
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Create an IndexableDocument instance from a file path
|
155
|
+
*/
|
156
|
+
public static async fromPath(
|
157
|
+
filePath: string,
|
158
|
+
contentCol: string = 'content',
|
159
|
+
metaCol: string = 'metadata'
|
160
|
+
): Promise<IndexableDocumentImpl> {
|
161
|
+
try {
|
162
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
163
|
+
let data: any[] | Record<string, any>;
|
164
|
+
|
165
|
+
// Parse data based on file extension
|
166
|
+
const ext = path.extname(filePath).toLowerCase();
|
167
|
+
switch (ext) {
|
168
|
+
case '.json':
|
169
|
+
data = JSON.parse(content);
|
170
|
+
break;
|
171
|
+
case '.csv':
|
172
|
+
// Simple CSV parsing (for more complex needs, use a CSV library)
|
173
|
+
data = content.split('\n').map(line => line.split(','));
|
174
|
+
break;
|
175
|
+
default:
|
176
|
+
data = [{ content }];
|
177
|
+
}
|
178
|
+
|
179
|
+
return new IndexableDocumentImpl({
|
180
|
+
content,
|
181
|
+
path: filePath,
|
182
|
+
name: path.basename(filePath),
|
183
|
+
data,
|
184
|
+
contentCol,
|
185
|
+
metaCol,
|
186
|
+
});
|
187
|
+
} catch (error: unknown) {
|
188
|
+
const message = error instanceof Error ? error.message : String(error);
|
189
|
+
throw new Error(`Failed to read file ${filePath}: ${message}`);
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* Get documents and metadata from the data
|
195
|
+
*/
|
196
|
+
public getDocsAndMetadata(): [string[], Record<string, any>[]] {
|
197
|
+
const docs: string[] = [];
|
198
|
+
const metadata: Record<string, any>[] = [];
|
199
|
+
|
200
|
+
if (Array.isArray(this.data)) {
|
201
|
+
// Handle array data
|
202
|
+
this.data.forEach(item => {
|
203
|
+
const content = typeof item === 'string' ? item : item[this.contentCol] || '';
|
204
|
+
docs.push(content);
|
205
|
+
metadata.push(this.metaCol && typeof item === 'object' ? { [this.metaCol]: item[this.metaCol] } : {});
|
206
|
+
});
|
207
|
+
} else {
|
208
|
+
// Handle record data
|
209
|
+
Object.entries(this.data).forEach(([key, value]) => {
|
210
|
+
const content = typeof value === 'string' ? value : value[this.contentCol] || '';
|
211
|
+
docs.push(content);
|
212
|
+
metadata.push(this.metaCol && typeof value === 'object' ? { [this.metaCol]: value[this.metaCol] } : {});
|
213
|
+
});
|
214
|
+
}
|
215
|
+
|
216
|
+
return [docs, metadata];
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Repository class implementation
|
222
|
+
*/
|
223
|
+
export class RepoImpl implements Repo {
|
224
|
+
public name: string = '';
|
225
|
+
public docs: Record<string, Document> = {};
|
226
|
+
public codes: Record<string, Document> = {};
|
227
|
+
public assets: Record<string, Document> = {};
|
228
|
+
public path?: string;
|
229
|
+
|
230
|
+
constructor(data: Partial<Repo> = {}) {
|
231
|
+
Object.assign(this, RepoSchema.parse(data));
|
232
|
+
}
|
233
|
+
|
234
|
+
private _path(filename: string): string {
|
235
|
+
if (!this.path) {
|
236
|
+
throw new Error('Repository path is not set.');
|
237
|
+
}
|
238
|
+
return path.join(this.path, filename);
|
239
|
+
}
|
240
|
+
|
241
|
+
/**
|
242
|
+
* Create a Repo instance from a directory path
|
243
|
+
*/
|
244
|
+
public static async fromPath(dirPath: string): Promise<RepoImpl> {
|
245
|
+
try {
|
246
|
+
await fs.mkdir(dirPath, { recursive: true });
|
247
|
+
const repo = new RepoImpl({
|
248
|
+
path: dirPath,
|
249
|
+
name: path.basename(dirPath),
|
250
|
+
});
|
251
|
+
|
252
|
+
// Read all files recursively
|
253
|
+
const readDir = async (dir: string) => {
|
254
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
255
|
+
for (const entry of entries) {
|
256
|
+
const fullPath = path.join(dir, entry.name);
|
257
|
+
if (entry.isDirectory()) {
|
258
|
+
await readDir(fullPath);
|
259
|
+
} else {
|
260
|
+
const ext = path.extname(entry.name).toLowerCase();
|
261
|
+
if (['.json', '.txt', '.md', '.py', '.js', '.css', '.html'].includes(ext)) {
|
262
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
263
|
+
await repo._set(content, fullPath);
|
264
|
+
}
|
265
|
+
}
|
266
|
+
}
|
267
|
+
};
|
268
|
+
|
269
|
+
await readDir(dirPath);
|
270
|
+
return repo;
|
271
|
+
} catch (error: unknown) {
|
272
|
+
const message = error instanceof Error ? error.message : String(error);
|
273
|
+
throw new Error(`Failed to create repository from ${dirPath}: ${message}`);
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Save all documents to disk
|
279
|
+
*/
|
280
|
+
public async toPath(): Promise<void> {
|
281
|
+
const saveAll = async (docs: Record<string, Document>) => {
|
282
|
+
for (const doc of Object.values(docs)) {
|
283
|
+
if (doc instanceof DocumentImpl && doc.toPath) {
|
284
|
+
await doc.toPath();
|
285
|
+
}
|
286
|
+
}
|
287
|
+
};
|
288
|
+
|
289
|
+
await Promise.all([
|
290
|
+
saveAll(this.docs),
|
291
|
+
saveAll(this.codes),
|
292
|
+
saveAll(this.assets),
|
293
|
+
]);
|
294
|
+
}
|
295
|
+
|
296
|
+
private async _set(content: string, filePath: string): Promise<Document> {
|
297
|
+
const ext = path.extname(filePath).toLowerCase();
|
298
|
+
const doc = new DocumentImpl({
|
299
|
+
content,
|
300
|
+
path: filePath,
|
301
|
+
name: path.relative(this.path || '', filePath),
|
302
|
+
});
|
303
|
+
|
304
|
+
if (ext === '.md') {
|
305
|
+
this.docs[filePath] = doc;
|
306
|
+
} else if (['.py', '.js', '.css', '.html'].includes(ext)) {
|
307
|
+
this.codes[filePath] = doc;
|
308
|
+
} else {
|
309
|
+
this.assets[filePath] = doc;
|
310
|
+
}
|
311
|
+
|
312
|
+
return doc;
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Set a document and persist it to disk
|
317
|
+
*/
|
318
|
+
public async set(filename: string, content: string): Promise<void> {
|
319
|
+
const filePath = this._path(filename);
|
320
|
+
const doc = await this._set(content, filePath);
|
321
|
+
await doc.toPath();
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Get a document by its filename
|
326
|
+
*/
|
327
|
+
public get(filename: string): Document | undefined {
|
328
|
+
const filePath = this._path(filename);
|
329
|
+
return this.docs[filePath] || this.codes[filePath] || this.assets[filePath];
|
330
|
+
}
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Get all text documents
|
334
|
+
*/
|
335
|
+
public getTextDocuments(): Document[] {
|
336
|
+
return [...Object.values(this.docs), ...Object.values(this.codes)];
|
337
|
+
}
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Get repository metadata
|
341
|
+
*/
|
342
|
+
public getMetadata(): RepoMetadata {
|
343
|
+
const nDocs = Object.keys(this.docs).length + Object.keys(this.codes).length + Object.keys(this.assets).length;
|
344
|
+
const nChars = [...Object.values(this.docs), ...Object.values(this.codes), ...Object.values(this.assets)]
|
345
|
+
.reduce((sum, doc) => sum + doc.content.length, 0);
|
346
|
+
|
347
|
+
return {
|
348
|
+
name: this.name,
|
349
|
+
nDocs,
|
350
|
+
nChars,
|
351
|
+
symbols: [], // TODO: Implement symbol extraction
|
352
|
+
};
|
353
|
+
}
|
354
|
+
}
|
package/src/types/llm.ts
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
/**
|
2
|
+
* @module LLM
|
3
|
+
* @category Core
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { z } from 'zod';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* LLM configuration schema
|
10
|
+
* Defines settings for language model operations
|
11
|
+
*/
|
12
|
+
export const LLMConfigSchema = z.object({
|
13
|
+
/** API key for authentication */
|
14
|
+
apiKey: z.string(),
|
15
|
+
/** Model name (e.g., 'gpt-4') */
|
16
|
+
model: z.string().default('gpt-4'),
|
17
|
+
/** Temperature for response generation */
|
18
|
+
temperature: z.number().min(0).max(2).default(0.7),
|
19
|
+
/** Maximum tokens in response */
|
20
|
+
maxTokens: z.number().min(1).default(2000),
|
21
|
+
/** Top P sampling */
|
22
|
+
topP: z.number().min(0).max(1).default(1),
|
23
|
+
/** Frequency penalty */
|
24
|
+
frequencyPenalty: z.number().min(-2).max(2).default(0),
|
25
|
+
/** Presence penalty */
|
26
|
+
presencePenalty: z.number().min(-2).max(2).default(0),
|
27
|
+
/** Base URL for API */
|
28
|
+
baseURL: z.string().optional(),
|
29
|
+
/** Organization ID */
|
30
|
+
organization: z.string().optional(),
|
31
|
+
/** Proxy URL */
|
32
|
+
proxy: z.string().optional(),
|
33
|
+
});
|
34
|
+
|
35
|
+
export type LLMConfig = z.infer<typeof LLMConfigSchema>;
|
36
|
+
|
37
|
+
/**
|
38
|
+
* LLM provider interface
|
39
|
+
* Defines methods that must be implemented by LLM providers
|
40
|
+
*/
|
41
|
+
export interface LLMProvider {
|
42
|
+
/**
|
43
|
+
* Generate text completion
|
44
|
+
* @param prompt - Input prompt
|
45
|
+
* @param config - Optional configuration overrides
|
46
|
+
* @returns Generated text
|
47
|
+
*/
|
48
|
+
generate(prompt: string, config?: Partial<LLMConfig>): Promise<string>;
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Generate text completion as a stream
|
52
|
+
* @param prompt - Input prompt
|
53
|
+
* @param config - Optional configuration overrides
|
54
|
+
* @returns Generated text stream
|
55
|
+
*/
|
56
|
+
generateStream(prompt: string, config?: Partial<LLMConfig>): AsyncIterable<string>;
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Create text embeddings
|
60
|
+
* @param text - Input text
|
61
|
+
* @returns Embedding vector
|
62
|
+
*/
|
63
|
+
embed(text: string): Promise<number[]>;
|
64
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import type { Message } from './message';
|
3
|
+
|
4
|
+
export const MemoryConfigSchema = z.object({
|
5
|
+
maxSize: z.number().optional(),
|
6
|
+
});
|
7
|
+
|
8
|
+
export type MemoryConfig = z.infer<typeof MemoryConfigSchema>;
|
9
|
+
|
10
|
+
export class ArrayMemory {
|
11
|
+
private messages: Message[] = [];
|
12
|
+
private config: MemoryConfig;
|
13
|
+
|
14
|
+
constructor(config: MemoryConfig = {}) {
|
15
|
+
this.config = MemoryConfigSchema.parse(config);
|
16
|
+
}
|
17
|
+
|
18
|
+
add(message: Message): void {
|
19
|
+
this.messages.push(message);
|
20
|
+
if (this.config.maxSize && this.messages.length > this.config.maxSize) {
|
21
|
+
this.messages.shift();
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
get(): Message[] {
|
26
|
+
return [...this.messages];
|
27
|
+
}
|
28
|
+
|
29
|
+
getByActions(actions: Set<string>): Message[] {
|
30
|
+
return this.messages.filter(msg => actions.has(msg.causedBy));
|
31
|
+
}
|
32
|
+
|
33
|
+
clear(): void {
|
34
|
+
this.messages = [];
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,193 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Message route constants
|
6
|
+
*/
|
7
|
+
export const MESSAGE_ROUTE = {
|
8
|
+
CAUSE_BY: 'UserRequirement',
|
9
|
+
FROM: '',
|
10
|
+
TO: '*',
|
11
|
+
TO_ALL: '*',
|
12
|
+
} as const;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Simple message schema without routing information
|
16
|
+
*/
|
17
|
+
export const SimpleMessageSchema = z.object({
|
18
|
+
content: z.string(),
|
19
|
+
role: z.string(),
|
20
|
+
});
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Base message schema with full routing and content information
|
24
|
+
*/
|
25
|
+
export const MessageSchema = z.object({
|
26
|
+
id: z.string().default('').transform(id => id || uuidv4()),
|
27
|
+
content: z.string(),
|
28
|
+
instructContent: z.any().optional().nullable(),
|
29
|
+
role: z.string().default('user'),
|
30
|
+
causedBy: z.string().default(MESSAGE_ROUTE.CAUSE_BY),
|
31
|
+
sentFrom: z.string().default(MESSAGE_ROUTE.FROM),
|
32
|
+
sendTo: z.union([
|
33
|
+
z.string(),
|
34
|
+
z.array(z.string()),
|
35
|
+
z.instanceof(Set),
|
36
|
+
]).default(MESSAGE_ROUTE.TO_ALL).transform(data => {
|
37
|
+
if (typeof data === 'string') {
|
38
|
+
return new Set([data]);
|
39
|
+
}
|
40
|
+
if (Array.isArray(data)) {
|
41
|
+
return new Set(data);
|
42
|
+
}
|
43
|
+
if (data instanceof Set) {
|
44
|
+
return data;
|
45
|
+
}
|
46
|
+
return new Set([MESSAGE_ROUTE.TO_ALL]);
|
47
|
+
}),
|
48
|
+
});
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Message types
|
52
|
+
*/
|
53
|
+
export type SimpleMessage = z.infer<typeof SimpleMessageSchema>;
|
54
|
+
export type Message = z.infer<typeof MessageSchema>;
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Message queue interface for handling asynchronous message updates
|
58
|
+
*/
|
59
|
+
export interface MessageQueue {
|
60
|
+
push(msg: Message): void;
|
61
|
+
pop(): Promise<Message | null>;
|
62
|
+
popAll(): Promise<Message[]>;
|
63
|
+
empty(): boolean;
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Message queue implementation using async queue
|
68
|
+
*/
|
69
|
+
export class AsyncMessageQueue implements MessageQueue {
|
70
|
+
private queue: Message[] = [];
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Push a message to the queue
|
74
|
+
* @param msg - Message to push
|
75
|
+
*/
|
76
|
+
public push(msg: Message): void {
|
77
|
+
this.queue.push(msg);
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Pop a message from the queue
|
82
|
+
* @returns Promise resolving to the next message or null if queue is empty
|
83
|
+
*/
|
84
|
+
public async pop(): Promise<Message | null> {
|
85
|
+
if (this.queue.length === 0) {
|
86
|
+
return null;
|
87
|
+
}
|
88
|
+
return this.queue.shift() || null;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Pop all messages from the queue
|
93
|
+
* @returns Promise resolving to array of all messages
|
94
|
+
*/
|
95
|
+
public async popAll(): Promise<Message[]> {
|
96
|
+
const messages = [...this.queue];
|
97
|
+
this.queue = [];
|
98
|
+
return messages;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Check if queue is empty
|
103
|
+
* @returns True if queue is empty
|
104
|
+
*/
|
105
|
+
public empty(): boolean {
|
106
|
+
return this.queue.length === 0;
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Convert queue to JSON string
|
111
|
+
* @returns JSON string representation of queue
|
112
|
+
*/
|
113
|
+
public async dump(): Promise<string> {
|
114
|
+
return JSON.stringify({
|
115
|
+
messages: this.queue.map(msg => ({
|
116
|
+
...msg,
|
117
|
+
sendTo: Array.from(msg.sendTo),
|
118
|
+
})),
|
119
|
+
});
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Create queue from JSON string
|
124
|
+
* @param data - JSON string representation of queue
|
125
|
+
* @returns New AsyncMessageQueue instance
|
126
|
+
*/
|
127
|
+
public static load(data: string): AsyncMessageQueue {
|
128
|
+
const queue = new AsyncMessageQueue();
|
129
|
+
try {
|
130
|
+
const parsed = JSON.parse(data);
|
131
|
+
if (Array.isArray(parsed.messages)) {
|
132
|
+
queue.queue = parsed.messages.map((msg: any) => ({
|
133
|
+
...msg,
|
134
|
+
sendTo: new Set(msg.sendTo),
|
135
|
+
}));
|
136
|
+
}
|
137
|
+
} catch (error) {
|
138
|
+
console.error('Failed to load message queue:', error);
|
139
|
+
}
|
140
|
+
return queue;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* User message with predefined role
|
146
|
+
*/
|
147
|
+
export class UserMessage implements Message {
|
148
|
+
public id: string = uuidv4();
|
149
|
+
public content: string;
|
150
|
+
public instructContent: any = null;
|
151
|
+
public role: string = 'user';
|
152
|
+
public causedBy: string = MESSAGE_ROUTE.CAUSE_BY;
|
153
|
+
public sentFrom: string = MESSAGE_ROUTE.FROM;
|
154
|
+
public sendTo: Set<string> = new Set([MESSAGE_ROUTE.TO_ALL]);
|
155
|
+
|
156
|
+
constructor(content: string) {
|
157
|
+
this.content = content;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
/**
|
162
|
+
* System message with predefined role
|
163
|
+
*/
|
164
|
+
export class SystemMessage implements Message {
|
165
|
+
public id: string = uuidv4();
|
166
|
+
public content: string;
|
167
|
+
public instructContent: any = null;
|
168
|
+
public role: string = 'system';
|
169
|
+
public causedBy: string = MESSAGE_ROUTE.CAUSE_BY;
|
170
|
+
public sentFrom: string = MESSAGE_ROUTE.FROM;
|
171
|
+
public sendTo: Set<string> = new Set([MESSAGE_ROUTE.TO_ALL]);
|
172
|
+
|
173
|
+
constructor(content: string) {
|
174
|
+
this.content = content;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
/**
|
179
|
+
* AI message with predefined role
|
180
|
+
*/
|
181
|
+
export class AIMessage implements Message {
|
182
|
+
public id: string = uuidv4();
|
183
|
+
public content: string;
|
184
|
+
public instructContent: any = null;
|
185
|
+
public role: string = 'assistant';
|
186
|
+
public causedBy: string = MESSAGE_ROUTE.CAUSE_BY;
|
187
|
+
public sentFrom: string = MESSAGE_ROUTE.FROM;
|
188
|
+
public sendTo: Set<string> = new Set([MESSAGE_ROUTE.TO_ALL]);
|
189
|
+
|
190
|
+
constructor(content: string) {
|
191
|
+
this.content = content;
|
192
|
+
}
|
193
|
+
}
|