@astrive-ai/memory 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ import { IProvider } from '@astrive-ai/types';
2
+ export interface EmbeddingResult {
3
+ text: string;
4
+ vector: number[];
5
+ }
6
+ export declare class EmbeddingModel {
7
+ private provider?;
8
+ private modelName;
9
+ constructor(provider?: IProvider, modelName?: string);
10
+ embedText(text: string): Promise<number[]>;
11
+ embedBatch(texts: string[]): Promise<EmbeddingResult[]>;
12
+ private mockEmbedding;
13
+ }
14
+ //# sourceMappingURL=embeddings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAY;IAC7B,OAAO,CAAC,SAAS,CAAS;gBAEd,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,GAAE,MAAiC;IAKjE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAY1C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IASpE,OAAO,CAAC,aAAa;CAiBtB"}
@@ -0,0 +1,42 @@
1
+ export class EmbeddingModel {
2
+ provider;
3
+ modelName;
4
+ constructor(provider, modelName = 'text-embedding-3-small') {
5
+ this.provider = provider;
6
+ this.modelName = modelName;
7
+ }
8
+ async embedText(text) {
9
+ if (!this.provider) {
10
+ // Mock embedding if no provider
11
+ return this.mockEmbedding(text, 1536);
12
+ }
13
+ // In a real implementation, this would call the provider's embedding endpoint
14
+ // For now, if the provider doesn't support embeddings explicitly, we mock it or
15
+ // implement a standard fetch to an embeddings API.
16
+ return this.mockEmbedding(text, 1536);
17
+ }
18
+ async embedBatch(texts) {
19
+ const results = [];
20
+ for (const text of texts) {
21
+ const vector = await this.embedText(text);
22
+ results.push({ text, vector });
23
+ }
24
+ return results;
25
+ }
26
+ mockEmbedding(text, dimensions) {
27
+ // Generate a deterministic pseudo-random vector based on text
28
+ let hash = 0;
29
+ for (let i = 0; i < text.length; i++) {
30
+ hash = ((hash << 5) - hash) + text.charCodeAt(i);
31
+ hash = hash & hash;
32
+ }
33
+ const vector = new Array(dimensions);
34
+ for (let i = 0; i < dimensions; i++) {
35
+ vector[i] = Math.sin(hash + i);
36
+ }
37
+ // Normalize
38
+ const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
39
+ return vector.map(val => val / (magnitude || 1));
40
+ }
41
+ }
42
+ //# sourceMappingURL=embeddings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,cAAc;IACjB,QAAQ,CAAa;IACrB,SAAS,CAAS;IAE1B,YAAY,QAAoB,EAAE,YAAoB,wBAAwB;QAC5E,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,SAAS,CAAC,IAAY;QACjC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,gCAAgC;YAChC,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,8EAA8E;QAC9E,iFAAiF;QACjF,mDAAmD;QACnD,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,KAAe;QACrC,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,aAAa,CAAC,IAAY,EAAE,UAAkB;QACpD,8DAA8D;QAC9D,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACjD,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;QAED,YAAY;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import { InMemoryStore } from './memory-store.js';
2
+ import { MemoryEntry } from '@astrive-ai/types';
3
+ export declare class FileMemoryStore extends InMemoryStore {
4
+ private filePath;
5
+ private saveTimeout;
6
+ constructor(filePath: string, maxEntries?: number);
7
+ init(): Promise<void>;
8
+ add(entry: MemoryEntry): Promise<string>;
9
+ update(id: string, partial: Partial<MemoryEntry>): Promise<void>;
10
+ delete(id: string): Promise<void>;
11
+ clear(): Promise<void>;
12
+ private scheduleSave;
13
+ private saveToDisk;
14
+ }
15
+ //# sourceMappingURL=file-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAA+B;gBAEtC,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAE,MAAa;IAK1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrB,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAMxC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnC,OAAO,CAAC,YAAY;YASN,UAAU;CAIzB"}
@@ -0,0 +1,54 @@
1
+ import { InMemoryStore } from './memory-store.js';
2
+ import fs from 'fs/promises';
3
+ export class FileMemoryStore extends InMemoryStore {
4
+ filePath;
5
+ saveTimeout = null;
6
+ constructor(filePath, maxEntries = 1000) {
7
+ super(maxEntries);
8
+ this.filePath = filePath;
9
+ }
10
+ async init() {
11
+ try {
12
+ const data = await fs.readFile(this.filePath, 'utf-8');
13
+ const parsed = JSON.parse(data);
14
+ for (const entry of parsed) {
15
+ this.entries.set(entry.id, entry);
16
+ }
17
+ }
18
+ catch (e) {
19
+ if (e.code !== 'ENOENT') {
20
+ throw e;
21
+ }
22
+ }
23
+ }
24
+ async add(entry) {
25
+ const id = await super.add(entry);
26
+ this.scheduleSave();
27
+ return id;
28
+ }
29
+ async update(id, partial) {
30
+ await super.update(id, partial);
31
+ this.scheduleSave();
32
+ }
33
+ async delete(id) {
34
+ await super.delete(id);
35
+ this.scheduleSave();
36
+ }
37
+ async clear() {
38
+ await super.clear();
39
+ this.scheduleSave();
40
+ }
41
+ scheduleSave() {
42
+ if (this.saveTimeout) {
43
+ clearTimeout(this.saveTimeout);
44
+ }
45
+ this.saveTimeout = setTimeout(() => {
46
+ this.saveToDisk();
47
+ }, 500);
48
+ }
49
+ async saveToDisk() {
50
+ const data = JSON.stringify(Array.from(this.entries.values()), null, 2);
51
+ await fs.writeFile(this.filePath, data, 'utf-8');
52
+ }
53
+ }
54
+ //# sourceMappingURL=file-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.js","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7B,MAAM,OAAO,eAAgB,SAAQ,aAAa;IACxC,QAAQ,CAAS;IACjB,WAAW,GAA0B,IAAI,CAAC;IAElD,YAAY,QAAgB,EAAE,aAAqB,IAAI;QACrD,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,MAAM,GAAkB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxB,MAAM,CAAC,CAAC;YACV,CAAC;QACH,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,KAAkB;QACjC,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,OAA6B;QAC3D,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,EAAU;QAC5B,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,YAAY;QAClB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YACjC,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export * from './memory-store.js';
2
+ export * from './file-store.js';
3
+ export * from './memory-manager.js';
4
+ export * from './embeddings.js';
5
+ export * from './vector-store.js';
6
+ export * from './reranker.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './memory-store.js';
2
+ export * from './file-store.js';
3
+ export * from './memory-manager.js';
4
+ export * from './embeddings.js';
5
+ export * from './vector-store.js';
6
+ export * from './reranker.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { MemoryConfig, MemoryType, MemoryMetadata, MemorySearchOptions, MemoryEntry, Message, ILogger, IEventEmitter } from '@astrive-ai/types';
2
+ export declare class MemoryManager {
3
+ private logger;
4
+ private events;
5
+ private store;
6
+ private vectorStore;
7
+ private reranker;
8
+ constructor(config: Partial<MemoryConfig> | undefined, logger: ILogger, events: IEventEmitter);
9
+ addMemory(content: string, type: MemoryType, metadata?: Partial<MemoryMetadata>): Promise<string>;
10
+ searchMemories(query: string, options?: MemorySearchOptions): Promise<MemoryEntry[]>;
11
+ getRelevantContext(prompt: string, limit?: number): Promise<string>;
12
+ extractAndSaveMemories(messages: Message[]): Promise<void>;
13
+ get(id: string): Promise<MemoryEntry | null>;
14
+ update(id: string, partial: Partial<MemoryEntry>): Promise<void>;
15
+ delete(id: string): Promise<void>;
16
+ clear(): Promise<void>;
17
+ getStats(): Promise<import("@astrive-ai/types").MemoryStats>;
18
+ }
19
+ //# sourceMappingURL=memory-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-manager.d.ts","sourceRoot":"","sources":["../src/memory-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,mBAAmB,EACnB,WAAW,EACX,OAAO,EACP,OAAO,EACP,aAAa,EACd,MAAM,mBAAmB,CAAC;AAO3B,qBAAa,aAAa;IAOtB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IAPhB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAW;gBAGzB,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,EACjC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,aAAa;IAgBlB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,GAAE,OAAO,CAAC,cAAc,CAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBrG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAKpF,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBtE,sBAAsB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1D,GAAG,CAAC,EAAE,EAAE,MAAM;IACd,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC;IAChD,MAAM,CAAC,EAAE,EAAE,MAAM;IACjB,KAAK;IACL,QAAQ;CACtB"}
@@ -0,0 +1,87 @@
1
+ import { InMemoryStore } from './memory-store.js';
2
+ import { FileMemoryStore } from './file-store.js';
3
+ import { VectorStore } from './vector-store.js';
4
+ import { Reranker } from './reranker.js';
5
+ import { randomUUID } from 'crypto';
6
+ export class MemoryManager {
7
+ logger;
8
+ events;
9
+ store;
10
+ vectorStore;
11
+ reranker;
12
+ constructor(config, logger, events) {
13
+ this.logger = logger;
14
+ this.events = events;
15
+ const maxEntries = config?.maxEntries || 1000;
16
+ if (config?.store === 'file' && config.filePath) {
17
+ const fileStore = new FileMemoryStore(config.filePath, maxEntries);
18
+ fileStore.init().catch(e => logger.error('Failed to init FileMemoryStore', e));
19
+ this.store = fileStore;
20
+ }
21
+ else {
22
+ this.store = new InMemoryStore(maxEntries);
23
+ }
24
+ this.vectorStore = new VectorStore();
25
+ this.reranker = new Reranker();
26
+ }
27
+ async addMemory(content, type, metadata = {}) {
28
+ const importance = Math.min(10, Math.ceil(content.length / 100)); // basic scoring
29
+ const fullMetadata = {
30
+ source: 'user',
31
+ importance,
32
+ tags: [],
33
+ ...metadata
34
+ };
35
+ const entry = {
36
+ id: randomUUID(),
37
+ content,
38
+ type,
39
+ metadata: fullMetadata,
40
+ createdAt: Date.now(),
41
+ updatedAt: Date.now()
42
+ };
43
+ const id = await this.store.add(entry);
44
+ await this.vectorStore.add(entry);
45
+ this.events.emit('memory:save', { id, type, importance });
46
+ this.logger.debug(`Saved memory ${id}`);
47
+ return id;
48
+ }
49
+ async searchMemories(query, options) {
50
+ this.events.emit('memory:search', { query, options });
51
+ return this.store.search(query, options);
52
+ }
53
+ async getRelevantContext(prompt, limit = 5) {
54
+ // Basic store search
55
+ const textMemories = await this.searchMemories(prompt, { limit });
56
+ // Vector search
57
+ const vectorResults = await this.vectorStore.search(prompt, limit * 2);
58
+ // Combine and deduplicate
59
+ const combined = new Map();
60
+ for (const m of textMemories) {
61
+ combined.set(m.id, { entry: m, score: 0.5 });
62
+ }
63
+ for (const v of vectorResults) {
64
+ combined.set(v.entry.id, v);
65
+ }
66
+ const combinedList = Array.from(combined.values());
67
+ // Rerank
68
+ const ranked = this.reranker.rerank(prompt, combinedList).slice(0, limit);
69
+ if (ranked.length === 0)
70
+ return '';
71
+ return ranked.map(r => r.entry.content).join('\n---\n');
72
+ }
73
+ async extractAndSaveMemories(messages) {
74
+ for (const msg of messages) {
75
+ if (msg.role === 'user' && msg.content.length > 50) {
76
+ await this.addMemory(msg.content, 'conversation', { tags: ['extracted'] });
77
+ }
78
+ }
79
+ }
80
+ // Delegated methods
81
+ async get(id) { return this.store.get(id); }
82
+ async update(id, partial) { return this.store.update(id, partial); }
83
+ async delete(id) { return this.store.delete(id); }
84
+ async clear() { return this.store.clear(); }
85
+ async getStats() { return this.store.getStats(); }
86
+ }
87
+ //# sourceMappingURL=memory-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-manager.js","sourceRoot":"","sources":["../src/memory-manager.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,MAAM,OAAO,aAAa;IAOd;IACA;IAPF,KAAK,CAAe;IACpB,WAAW,CAAc;IACzB,QAAQ,CAAW;IAE3B,YACE,MAAyC,EACjC,MAAe,EACf,MAAqB;QADrB,WAAM,GAAN,MAAM,CAAS;QACf,WAAM,GAAN,MAAM,CAAe;QAE7B,MAAM,UAAU,GAAG,MAAM,EAAE,UAAU,IAAI,IAAI,CAAC;QAE9C,IAAI,MAAM,EAAE,KAAK,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChD,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YACnE,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/E,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IACjC,CAAC;IAEM,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,IAAgB,EAAE,WAAoC,EAAE;QAC9F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB;QAClF,MAAM,YAAY,GAAmB;YACnC,MAAM,EAAE,MAAM;YACd,UAAU;YACV,IAAI,EAAE,EAAE;YACR,GAAG,QAAQ;SACZ,CAAC;QAEF,MAAM,KAAK,GAAgB;YACzB,EAAE,EAAE,UAAU,EAAE;YAChB,OAAO;YACP,IAAI;YACJ,QAAQ,EAAE,YAAY;YACtB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAEM,KAAK,CAAC,cAAc,CAAC,KAAa,EAAE,OAA6B;QACtE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,QAAgB,CAAC;QAC/D,qBAAqB;QACrB,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAElE,gBAAgB;QAChB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAEvE,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAe,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,SAAS;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAE1E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAC,QAAmB;QACrD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;gBACnD,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;IAED,oBAAoB;IACb,KAAK,CAAC,GAAG,CAAC,EAAU,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,OAA6B,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAClG,KAAK,CAAC,MAAM,CAAC,EAAU,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,KAAK,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;CAC1D"}
@@ -0,0 +1,15 @@
1
+ import { IMemoryStore, MemoryEntry, MemorySearchOptions, MemoryStats } from '@astrive-ai/types';
2
+ export declare class InMemoryStore implements IMemoryStore {
3
+ protected entries: Map<string, MemoryEntry>;
4
+ protected maxEntries: number;
5
+ constructor(maxEntries?: number);
6
+ add(entry: MemoryEntry): Promise<string>;
7
+ search(query: string, options?: MemorySearchOptions): Promise<MemoryEntry[]>;
8
+ get(id: string): Promise<MemoryEntry | null>;
9
+ update(id: string, partial: Partial<MemoryEntry>): Promise<void>;
10
+ delete(id: string): Promise<void>;
11
+ clear(): Promise<void>;
12
+ getStats(): Promise<MemoryStats>;
13
+ private evictOldest;
14
+ }
15
+ //# sourceMappingURL=memory-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,aAAc,YAAW,YAAY;IAChD,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAa;IACxD,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEjB,UAAU,GAAE,MAAa;IAIxB,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAcxC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA+B5E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAI5C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAgB7C,OAAO,CAAC,WAAW;CAgBpB"}
@@ -0,0 +1,91 @@
1
+ export class InMemoryStore {
2
+ entries = new Map();
3
+ maxEntries;
4
+ constructor(maxEntries = 1000) {
5
+ this.maxEntries = maxEntries;
6
+ }
7
+ async add(entry) {
8
+ if (this.entries.size >= this.maxEntries) {
9
+ this.evictOldest();
10
+ }
11
+ // Ensure entry has an id if not provided, though the type might require it.
12
+ // If it doesn't have an ID, we'll generate one (useful if called loosely).
13
+ const id = entry.id || `mem_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
14
+ const newEntry = { ...entry, id };
15
+ this.entries.set(id, newEntry);
16
+ return id;
17
+ }
18
+ async search(query, options) {
19
+ const keywords = query.toLowerCase().split(/\s+/).filter(w => w.length > 2);
20
+ const results = [];
21
+ for (const entry of this.entries.values()) {
22
+ let score = 0;
23
+ const content = entry.content.toLowerCase();
24
+ for (const kw of keywords) {
25
+ if (content.includes(kw)) {
26
+ score += 1;
27
+ }
28
+ }
29
+ if (score > 0) {
30
+ if (options?.types && !options.types.includes(entry.type))
31
+ continue;
32
+ if (options?.tags && !options.tags.some(t => entry.metadata?.tags?.includes(t)))
33
+ continue;
34
+ if (options?.dateRange) {
35
+ const time = entry.createdAt;
36
+ if (options.dateRange.from && time < options.dateRange.from)
37
+ continue;
38
+ if (options.dateRange.to && time > options.dateRange.to)
39
+ continue;
40
+ }
41
+ results.push({ entry, score });
42
+ }
43
+ }
44
+ results.sort((a, b) => b.score - a.score);
45
+ const limit = options?.limit || 10;
46
+ return results.slice(0, limit).map(r => r.entry);
47
+ }
48
+ async get(id) {
49
+ return this.entries.get(id) || null;
50
+ }
51
+ async update(id, partial) {
52
+ const entry = this.entries.get(id);
53
+ if (entry) {
54
+ this.entries.set(id, { ...entry, ...partial, updatedAt: Date.now() });
55
+ }
56
+ }
57
+ async delete(id) {
58
+ this.entries.delete(id);
59
+ }
60
+ async clear() {
61
+ this.entries.clear();
62
+ }
63
+ async getStats() {
64
+ let size = 0;
65
+ const byType = {};
66
+ for (const entry of this.entries.values()) {
67
+ size += entry.content.length;
68
+ byType[entry.type] = (byType[entry.type] || 0) + 1;
69
+ }
70
+ return {
71
+ totalEntries: this.entries.size,
72
+ storageBytes: size,
73
+ byType
74
+ };
75
+ }
76
+ evictOldest() {
77
+ let oldestId = null;
78
+ let oldestTime = Infinity;
79
+ for (const [id, entry] of this.entries.entries()) {
80
+ const time = entry.createdAt || 0;
81
+ if (time < oldestTime) {
82
+ oldestTime = time;
83
+ oldestId = id;
84
+ }
85
+ }
86
+ if (oldestId) {
87
+ this.entries.delete(oldestId);
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=memory-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { VectorSearchResult } from './vector-store.js';
2
+ export declare class Reranker {
3
+ /**
4
+ * Reranks a set of search results based on a combined score of vector similarity,
5
+ * recency, and importance.
6
+ */
7
+ rerank(query: string, results: VectorSearchResult[]): VectorSearchResult[];
8
+ }
9
+ //# sourceMappingURL=reranker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reranker.d.ts","sourceRoot":"","sources":["../src/reranker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,qBAAa,QAAQ;IACnB;;;OAGG;IACI,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,kBAAkB,EAAE;CAoClF"}
@@ -0,0 +1,37 @@
1
+ export class Reranker {
2
+ /**
3
+ * Reranks a set of search results based on a combined score of vector similarity,
4
+ * recency, and importance.
5
+ */
6
+ rerank(query, results) {
7
+ const now = Date.now();
8
+ return results.map(result => {
9
+ const { entry, score: simScore } = result;
10
+ // Calculate recency score (0.0 to 1.0)
11
+ // Exponential decay over time (e.g., half-life of 7 days)
12
+ const ageMs = Math.max(0, now - entry.updatedAt);
13
+ const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
14
+ const recencyScore = Math.exp(-ageMs / sevenDaysMs);
15
+ // Get importance from metadata (0.0 to 1.0, default 0.5)
16
+ const importance = typeof entry.metadata?.importance === 'number'
17
+ ? entry.metadata.importance
18
+ : 0.5;
19
+ // Also slight boost if query keywords match content directly (lexical match)
20
+ const queryTerms = query.toLowerCase().split(/\s+/).filter(t => t.length > 2);
21
+ const contentLower = entry.content.toLowerCase();
22
+ let lexicalScore = 0;
23
+ for (const term of queryTerms) {
24
+ if (contentLower.includes(term))
25
+ lexicalScore += 0.1;
26
+ }
27
+ lexicalScore = Math.min(1.0, lexicalScore);
28
+ // Final combined score weights
29
+ const finalScore = (simScore * 0.6) +
30
+ (recencyScore * 0.15) +
31
+ (importance * 0.15) +
32
+ (lexicalScore * 0.10);
33
+ return { entry, score: finalScore };
34
+ }).sort((a, b) => b.score - a.score);
35
+ }
36
+ }
37
+ //# sourceMappingURL=reranker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reranker.js","sourceRoot":"","sources":["../src/reranker.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,QAAQ;IACnB;;;OAGG;IACI,MAAM,CAAC,KAAa,EAAE,OAA6B;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YAC1B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;YAE1C,uCAAuC;YACvC,0DAA0D;YAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;YAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;YAEpD,yDAAyD;YACzD,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,QAAQ,EAAE,UAAU,KAAK,QAAQ;gBAC/D,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU;gBAC3B,CAAC,CAAC,GAAG,CAAC;YAER,6EAA6E;YAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC9E,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC9B,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,YAAY,IAAI,GAAG,CAAC;YACvD,CAAC;YACD,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YAE3C,+BAA+B;YAC/B,MAAM,UAAU,GACd,CAAC,QAAQ,GAAG,GAAG,CAAC;gBAChB,CAAC,YAAY,GAAG,IAAI,CAAC;gBACrB,CAAC,UAAU,GAAG,IAAI,CAAC;gBACnB,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;YAExB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACtC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { MemoryEntry } from '@astrive-ai/types';
2
+ import { EmbeddingModel } from './embeddings.js';
3
+ export interface VectorSearchResult {
4
+ entry: MemoryEntry;
5
+ score: number;
6
+ }
7
+ export declare class VectorStore {
8
+ private entries;
9
+ private embeddingModel;
10
+ constructor(embeddingModel?: EmbeddingModel);
11
+ add(entry: MemoryEntry): Promise<string>;
12
+ addBatch(entries: MemoryEntry[]): Promise<string[]>;
13
+ search(query: string, topK?: number): Promise<VectorSearchResult[]>;
14
+ get(id: string): MemoryEntry | undefined;
15
+ delete(id: string): boolean;
16
+ private cosineSimilarity;
17
+ }
18
+ //# sourceMappingURL=vector-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-store.d.ts","sourceRoot":"","sources":["../src/vector-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,cAAc,CAAiB;gBAE3B,cAAc,CAAC,EAAE,cAAc;IAI9B,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAgB5E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIxC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIlC,OAAO,CAAC,gBAAgB;CAgBzB"}
@@ -0,0 +1,57 @@
1
+ import { EmbeddingModel } from './embeddings.js';
2
+ export class VectorStore {
3
+ entries = new Map();
4
+ embeddingModel;
5
+ constructor(embeddingModel) {
6
+ this.embeddingModel = embeddingModel || new EmbeddingModel();
7
+ }
8
+ async add(entry) {
9
+ if (!entry.embedding) {
10
+ entry.embedding = await this.embeddingModel.embedText(entry.content);
11
+ }
12
+ this.entries.set(entry.id, entry);
13
+ return entry.id;
14
+ }
15
+ async addBatch(entries) {
16
+ const ids = [];
17
+ for (const entry of entries) {
18
+ ids.push(await this.add(entry));
19
+ }
20
+ return ids;
21
+ }
22
+ async search(query, topK = 5) {
23
+ const queryVector = await this.embeddingModel.embedText(query);
24
+ const results = [];
25
+ for (const entry of this.entries.values()) {
26
+ if (entry.embedding) {
27
+ const score = this.cosineSimilarity(queryVector, entry.embedding);
28
+ results.push({ entry, score });
29
+ }
30
+ }
31
+ return results
32
+ .sort((a, b) => b.score - a.score)
33
+ .slice(0, topK);
34
+ }
35
+ get(id) {
36
+ return this.entries.get(id);
37
+ }
38
+ delete(id) {
39
+ return this.entries.delete(id);
40
+ }
41
+ cosineSimilarity(vecA, vecB) {
42
+ if (vecA.length !== vecB.length)
43
+ return 0;
44
+ let dotProduct = 0;
45
+ let normA = 0;
46
+ let normB = 0;
47
+ for (let i = 0; i < vecA.length; i++) {
48
+ dotProduct += vecA[i] * vecB[i];
49
+ normA += vecA[i] * vecA[i];
50
+ normB += vecB[i] * vecB[i];
51
+ }
52
+ if (normA === 0 || normB === 0)
53
+ return 0;
54
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
55
+ }
56
+ }
57
+ //# sourceMappingURL=vector-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../src/vector-store.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@astrive-ai/memory",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@astrive-ai/types": "*",
19
+ "@astrive-ai/logger": "*",
20
+ "@astrive-ai/events": "*"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "5.4.5",
24
+ "@types/node": "^20.0.0"
25
+ }
26
+ }
@@ -0,0 +1,55 @@
1
+ import { IProvider } from '@astrive-ai/types';
2
+
3
+ export interface EmbeddingResult {
4
+ text: string;
5
+ vector: number[];
6
+ }
7
+
8
+ export class EmbeddingModel {
9
+ private provider?: IProvider;
10
+ private modelName: string;
11
+
12
+ constructor(provider?: IProvider, modelName: string = 'text-embedding-3-small') {
13
+ this.provider = provider;
14
+ this.modelName = modelName;
15
+ }
16
+
17
+ public async embedText(text: string): Promise<number[]> {
18
+ if (!this.provider) {
19
+ // Mock embedding if no provider
20
+ return this.mockEmbedding(text, 1536);
21
+ }
22
+
23
+ // In a real implementation, this would call the provider's embedding endpoint
24
+ // For now, if the provider doesn't support embeddings explicitly, we mock it or
25
+ // implement a standard fetch to an embeddings API.
26
+ return this.mockEmbedding(text, 1536);
27
+ }
28
+
29
+ public async embedBatch(texts: string[]): Promise<EmbeddingResult[]> {
30
+ const results: EmbeddingResult[] = [];
31
+ for (const text of texts) {
32
+ const vector = await this.embedText(text);
33
+ results.push({ text, vector });
34
+ }
35
+ return results;
36
+ }
37
+
38
+ private mockEmbedding(text: string, dimensions: number): number[] {
39
+ // Generate a deterministic pseudo-random vector based on text
40
+ let hash = 0;
41
+ for (let i = 0; i < text.length; i++) {
42
+ hash = ((hash << 5) - hash) + text.charCodeAt(i);
43
+ hash = hash & hash;
44
+ }
45
+
46
+ const vector = new Array(dimensions);
47
+ for (let i = 0; i < dimensions; i++) {
48
+ vector[i] = Math.sin(hash + i);
49
+ }
50
+
51
+ // Normalize
52
+ const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
53
+ return vector.map(val => val / (magnitude || 1));
54
+ }
55
+ }
@@ -0,0 +1,62 @@
1
+ import { InMemoryStore } from './memory-store.js';
2
+ import { MemoryEntry } from '@astrive-ai/types';
3
+ import fs from 'fs/promises';
4
+
5
+ export class FileMemoryStore extends InMemoryStore {
6
+ private filePath: string;
7
+ private saveTimeout: NodeJS.Timeout | null = null;
8
+
9
+ constructor(filePath: string, maxEntries: number = 1000) {
10
+ super(maxEntries);
11
+ this.filePath = filePath;
12
+ }
13
+
14
+ public async init(): Promise<void> {
15
+ try {
16
+ const data = await fs.readFile(this.filePath, 'utf-8');
17
+ const parsed: MemoryEntry[] = JSON.parse(data);
18
+ for (const entry of parsed) {
19
+ this.entries.set(entry.id, entry);
20
+ }
21
+ } catch (e: any) {
22
+ if (e.code !== 'ENOENT') {
23
+ throw e;
24
+ }
25
+ }
26
+ }
27
+
28
+ public async add(entry: MemoryEntry): Promise<string> {
29
+ const id = await super.add(entry);
30
+ this.scheduleSave();
31
+ return id;
32
+ }
33
+
34
+ public async update(id: string, partial: Partial<MemoryEntry>): Promise<void> {
35
+ await super.update(id, partial);
36
+ this.scheduleSave();
37
+ }
38
+
39
+ public async delete(id: string): Promise<void> {
40
+ await super.delete(id);
41
+ this.scheduleSave();
42
+ }
43
+
44
+ public async clear(): Promise<void> {
45
+ await super.clear();
46
+ this.scheduleSave();
47
+ }
48
+
49
+ private scheduleSave(): void {
50
+ if (this.saveTimeout) {
51
+ clearTimeout(this.saveTimeout);
52
+ }
53
+ this.saveTimeout = setTimeout(() => {
54
+ this.saveToDisk();
55
+ }, 500);
56
+ }
57
+
58
+ private async saveToDisk(): Promise<void> {
59
+ const data = JSON.stringify(Array.from(this.entries.values()), null, 2);
60
+ await fs.writeFile(this.filePath, data, 'utf-8');
61
+ }
62
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './memory-store.js';
2
+ export * from './file-store.js';
3
+ export * from './memory-manager.js';
4
+ export * from './embeddings.js';
5
+ export * from './vector-store.js';
6
+ export * from './reranker.js';
@@ -0,0 +1,111 @@
1
+ import {
2
+ IMemoryStore,
3
+ MemoryConfig,
4
+ MemoryType,
5
+ MemoryMetadata,
6
+ MemorySearchOptions,
7
+ MemoryEntry,
8
+ Message,
9
+ ILogger,
10
+ IEventEmitter
11
+ } from '@astrive-ai/types';
12
+ import { InMemoryStore } from './memory-store.js';
13
+ import { FileMemoryStore } from './file-store.js';
14
+ import { VectorStore } from './vector-store.js';
15
+ import { Reranker } from './reranker.js';
16
+ import { randomUUID } from 'crypto';
17
+
18
+ export class MemoryManager {
19
+ private store: IMemoryStore;
20
+ private vectorStore: VectorStore;
21
+ private reranker: Reranker;
22
+
23
+ constructor(
24
+ config: Partial<MemoryConfig> | undefined,
25
+ private logger: ILogger,
26
+ private events: IEventEmitter
27
+ ) {
28
+ const maxEntries = config?.maxEntries || 1000;
29
+
30
+ if (config?.store === 'file' && config.filePath) {
31
+ const fileStore = new FileMemoryStore(config.filePath, maxEntries);
32
+ fileStore.init().catch(e => logger.error('Failed to init FileMemoryStore', e));
33
+ this.store = fileStore;
34
+ } else {
35
+ this.store = new InMemoryStore(maxEntries);
36
+ }
37
+
38
+ this.vectorStore = new VectorStore();
39
+ this.reranker = new Reranker();
40
+ }
41
+
42
+ public async addMemory(content: string, type: MemoryType, metadata: Partial<MemoryMetadata> = {}): Promise<string> {
43
+ const importance = Math.min(10, Math.ceil(content.length / 100)); // basic scoring
44
+ const fullMetadata: MemoryMetadata = {
45
+ source: 'user',
46
+ importance,
47
+ tags: [],
48
+ ...metadata
49
+ };
50
+
51
+ const entry: MemoryEntry = {
52
+ id: randomUUID(),
53
+ content,
54
+ type,
55
+ metadata: fullMetadata,
56
+ createdAt: Date.now(),
57
+ updatedAt: Date.now()
58
+ };
59
+
60
+ const id = await this.store.add(entry);
61
+ await this.vectorStore.add(entry);
62
+ this.events.emit('memory:save', { id, type, importance });
63
+ this.logger.debug(`Saved memory ${id}`);
64
+ return id;
65
+ }
66
+
67
+ public async searchMemories(query: string, options?: MemorySearchOptions): Promise<MemoryEntry[]> {
68
+ this.events.emit('memory:search', { query, options });
69
+ return this.store.search(query, options);
70
+ }
71
+
72
+ public async getRelevantContext(prompt: string, limit: number = 5): Promise<string> {
73
+ // Basic store search
74
+ const textMemories = await this.searchMemories(prompt, { limit });
75
+
76
+ // Vector search
77
+ const vectorResults = await this.vectorStore.search(prompt, limit * 2);
78
+
79
+ // Combine and deduplicate
80
+ const combined = new Map<string, any>();
81
+ for (const m of textMemories) {
82
+ combined.set(m.id, { entry: m, score: 0.5 });
83
+ }
84
+ for (const v of vectorResults) {
85
+ combined.set(v.entry.id, v);
86
+ }
87
+
88
+ const combinedList = Array.from(combined.values());
89
+
90
+ // Rerank
91
+ const ranked = this.reranker.rerank(prompt, combinedList).slice(0, limit);
92
+
93
+ if (ranked.length === 0) return '';
94
+ return ranked.map(r => r.entry.content).join('\n---\n');
95
+ }
96
+
97
+ public async extractAndSaveMemories(messages: Message[]): Promise<void> {
98
+ for (const msg of messages) {
99
+ if (msg.role === 'user' && msg.content.length > 50) {
100
+ await this.addMemory(msg.content, 'conversation', { tags: ['extracted'] });
101
+ }
102
+ }
103
+ }
104
+
105
+ // Delegated methods
106
+ public async get(id: string) { return this.store.get(id); }
107
+ public async update(id: string, partial: Partial<MemoryEntry>) { return this.store.update(id, partial); }
108
+ public async delete(id: string) { return this.store.delete(id); }
109
+ public async clear() { return this.store.clear(); }
110
+ public async getStats() { return this.store.getStats(); }
111
+ }
@@ -0,0 +1,113 @@
1
+ import {
2
+ IMemoryStore,
3
+ MemoryEntry,
4
+ MemorySearchOptions,
5
+ MemoryStats,
6
+ MemoryType
7
+ } from '@astrive-ai/types';
8
+
9
+ export class InMemoryStore implements IMemoryStore {
10
+ protected entries: Map<string, MemoryEntry> = new Map();
11
+ protected maxEntries: number;
12
+
13
+ constructor(maxEntries: number = 1000) {
14
+ this.maxEntries = maxEntries;
15
+ }
16
+
17
+ public async add(entry: MemoryEntry): Promise<string> {
18
+ if (this.entries.size >= this.maxEntries) {
19
+ this.evictOldest();
20
+ }
21
+
22
+ // Ensure entry has an id if not provided, though the type might require it.
23
+ // If it doesn't have an ID, we'll generate one (useful if called loosely).
24
+ const id = entry.id || `mem_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
25
+ const newEntry: MemoryEntry = { ...entry, id };
26
+
27
+ this.entries.set(id, newEntry);
28
+ return id;
29
+ }
30
+
31
+ public async search(query: string, options?: MemorySearchOptions): Promise<MemoryEntry[]> {
32
+ const keywords = query.toLowerCase().split(/\s+/).filter(w => w.length > 2);
33
+ const results: { entry: MemoryEntry; score: number }[] = [];
34
+
35
+ for (const entry of this.entries.values()) {
36
+ let score = 0;
37
+ const content = entry.content.toLowerCase();
38
+
39
+ for (const kw of keywords) {
40
+ if (content.includes(kw)) {
41
+ score += 1;
42
+ }
43
+ }
44
+
45
+ if (score > 0) {
46
+ if (options?.types && !options.types.includes(entry.type)) continue;
47
+ if (options?.tags && !options.tags.some(t => entry.metadata?.tags?.includes(t))) continue;
48
+ if (options?.dateRange) {
49
+ const time = entry.createdAt;
50
+ if (options.dateRange.from && time < options.dateRange.from) continue;
51
+ if (options.dateRange.to && time > options.dateRange.to) continue;
52
+ }
53
+ results.push({ entry, score });
54
+ }
55
+ }
56
+
57
+ results.sort((a, b) => b.score - a.score);
58
+ const limit = options?.limit || 10;
59
+ return results.slice(0, limit).map(r => r.entry);
60
+ }
61
+
62
+ public async get(id: string): Promise<MemoryEntry | null> {
63
+ return this.entries.get(id) || null;
64
+ }
65
+
66
+ public async update(id: string, partial: Partial<MemoryEntry>): Promise<void> {
67
+ const entry = this.entries.get(id);
68
+ if (entry) {
69
+ this.entries.set(id, { ...entry, ...partial, updatedAt: Date.now() });
70
+ }
71
+ }
72
+
73
+ public async delete(id: string): Promise<void> {
74
+ this.entries.delete(id);
75
+ }
76
+
77
+ public async clear(): Promise<void> {
78
+ this.entries.clear();
79
+ }
80
+
81
+ public async getStats(): Promise<MemoryStats> {
82
+ let size = 0;
83
+ const byType: Record<MemoryType, number> = {} as any;
84
+
85
+ for (const entry of this.entries.values()) {
86
+ size += entry.content.length;
87
+ byType[entry.type] = (byType[entry.type] || 0) + 1;
88
+ }
89
+
90
+ return {
91
+ totalEntries: this.entries.size,
92
+ storageBytes: size,
93
+ byType
94
+ };
95
+ }
96
+
97
+ private evictOldest(): void {
98
+ let oldestId: string | null = null;
99
+ let oldestTime = Infinity;
100
+
101
+ for (const [id, entry] of this.entries.entries()) {
102
+ const time = entry.createdAt || 0;
103
+ if (time < oldestTime) {
104
+ oldestTime = time;
105
+ oldestId = id;
106
+ }
107
+ }
108
+
109
+ if (oldestId) {
110
+ this.entries.delete(oldestId);
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,45 @@
1
+ import { MemoryEntry } from '@astrive-ai/types';
2
+ import { VectorSearchResult } from './vector-store.js';
3
+
4
+ export class Reranker {
5
+ /**
6
+ * Reranks a set of search results based on a combined score of vector similarity,
7
+ * recency, and importance.
8
+ */
9
+ public rerank(query: string, results: VectorSearchResult[]): VectorSearchResult[] {
10
+ const now = Date.now();
11
+
12
+ return results.map(result => {
13
+ const { entry, score: simScore } = result;
14
+
15
+ // Calculate recency score (0.0 to 1.0)
16
+ // Exponential decay over time (e.g., half-life of 7 days)
17
+ const ageMs = Math.max(0, now - entry.updatedAt);
18
+ const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
19
+ const recencyScore = Math.exp(-ageMs / sevenDaysMs);
20
+
21
+ // Get importance from metadata (0.0 to 1.0, default 0.5)
22
+ const importance = typeof entry.metadata?.importance === 'number'
23
+ ? entry.metadata.importance
24
+ : 0.5;
25
+
26
+ // Also slight boost if query keywords match content directly (lexical match)
27
+ const queryTerms = query.toLowerCase().split(/\s+/).filter(t => t.length > 2);
28
+ const contentLower = entry.content.toLowerCase();
29
+ let lexicalScore = 0;
30
+ for (const term of queryTerms) {
31
+ if (contentLower.includes(term)) lexicalScore += 0.1;
32
+ }
33
+ lexicalScore = Math.min(1.0, lexicalScore);
34
+
35
+ // Final combined score weights
36
+ const finalScore =
37
+ (simScore * 0.6) +
38
+ (recencyScore * 0.15) +
39
+ (importance * 0.15) +
40
+ (lexicalScore * 0.10);
41
+
42
+ return { entry, score: finalScore };
43
+ }).sort((a, b) => b.score - a.score);
44
+ }
45
+ }
@@ -0,0 +1,73 @@
1
+ import { MemoryEntry } from '@astrive-ai/types';
2
+ import { EmbeddingModel } from './embeddings.js';
3
+
4
+ export interface VectorSearchResult {
5
+ entry: MemoryEntry;
6
+ score: number;
7
+ }
8
+
9
+ export class VectorStore {
10
+ private entries: Map<string, MemoryEntry> = new Map();
11
+ private embeddingModel: EmbeddingModel;
12
+
13
+ constructor(embeddingModel?: EmbeddingModel) {
14
+ this.embeddingModel = embeddingModel || new EmbeddingModel();
15
+ }
16
+
17
+ public async add(entry: MemoryEntry): Promise<string> {
18
+ if (!entry.embedding) {
19
+ entry.embedding = await this.embeddingModel.embedText(entry.content);
20
+ }
21
+ this.entries.set(entry.id, entry);
22
+ return entry.id;
23
+ }
24
+
25
+ public async addBatch(entries: MemoryEntry[]): Promise<string[]> {
26
+ const ids: string[] = [];
27
+ for (const entry of entries) {
28
+ ids.push(await this.add(entry));
29
+ }
30
+ return ids;
31
+ }
32
+
33
+ public async search(query: string, topK: number = 5): Promise<VectorSearchResult[]> {
34
+ const queryVector = await this.embeddingModel.embedText(query);
35
+ const results: VectorSearchResult[] = [];
36
+
37
+ for (const entry of this.entries.values()) {
38
+ if (entry.embedding) {
39
+ const score = this.cosineSimilarity(queryVector, entry.embedding);
40
+ results.push({ entry, score });
41
+ }
42
+ }
43
+
44
+ return results
45
+ .sort((a, b) => b.score - a.score)
46
+ .slice(0, topK);
47
+ }
48
+
49
+ public get(id: string): MemoryEntry | undefined {
50
+ return this.entries.get(id);
51
+ }
52
+
53
+ public delete(id: string): boolean {
54
+ return this.entries.delete(id);
55
+ }
56
+
57
+ private cosineSimilarity(vecA: number[], vecB: number[]): number {
58
+ if (vecA.length !== vecB.length) return 0;
59
+
60
+ let dotProduct = 0;
61
+ let normA = 0;
62
+ let normB = 0;
63
+
64
+ for (let i = 0; i < vecA.length; i++) {
65
+ dotProduct += vecA[i] * vecB[i];
66
+ normA += vecA[i] * vecA[i];
67
+ normB += vecB[i] * vecB[i];
68
+ }
69
+
70
+ if (normA === 0 || normB === 0) return 0;
71
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
72
+ }
73
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }