@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.
- package/dist/embeddings.d.ts +14 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +42 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/file-store.d.ts +15 -0
- package/dist/file-store.d.ts.map +1 -0
- package/dist/file-store.js +54 -0
- package/dist/file-store.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-manager.d.ts +19 -0
- package/dist/memory-manager.d.ts.map +1 -0
- package/dist/memory-manager.js +87 -0
- package/dist/memory-manager.js.map +1 -0
- package/dist/memory-store.d.ts +15 -0
- package/dist/memory-store.d.ts.map +1 -0
- package/dist/memory-store.js +91 -0
- package/dist/memory-store.js.map +1 -0
- package/dist/reranker.d.ts +9 -0
- package/dist/reranker.d.ts.map +1 -0
- package/dist/reranker.js +37 -0
- package/dist/reranker.js.map +1 -0
- package/dist/vector-store.d.ts +18 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +57 -0
- package/dist/vector-store.js.map +1 -0
- package/package.json +26 -0
- package/src/embeddings.ts +55 -0
- package/src/file-store.ts +62 -0
- package/src/index.ts +6 -0
- package/src/memory-manager.ts +111 -0
- package/src/memory-store.ts +113 -0
- package/src/reranker.ts +45 -0
- package/src/vector-store.ts +73 -0
- package/tsconfig.json +8 -0
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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":"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"}
|
|
@@ -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"}
|
package/dist/reranker.js
ADDED
|
@@ -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,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
|
+
}
|
package/src/reranker.ts
ADDED
|
@@ -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
|
+
}
|