@cartisien/engram 0.1.0
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/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cartisien Interactive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Engram
|
|
2
|
+
|
|
3
|
+
> **Persistent memory for AI assistants.**
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Engram } from '@cartisien/engram';
|
|
7
|
+
|
|
8
|
+
const memory = new Engram({ dbPath: './memory.db' });
|
|
9
|
+
|
|
10
|
+
// Store
|
|
11
|
+
await memory.remember('user_123', 'I ride a Triumph Bonneville', 'user');
|
|
12
|
+
|
|
13
|
+
// Recall
|
|
14
|
+
const context = await memory.recall('user_123', 'What motorcycle?', 5);
|
|
15
|
+
// [{ role: 'user', content: 'I ride a Triumph Bonneville', ... }]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## The Problem
|
|
21
|
+
|
|
22
|
+
AI assistants are amnesiacs. Every conversation starts fresh. Context windows fill up. Important details get lost.
|
|
23
|
+
|
|
24
|
+
You wouldn't hire an employee who forgot every meeting. Why accept it from your AI?
|
|
25
|
+
|
|
26
|
+
## The Solution
|
|
27
|
+
|
|
28
|
+
Engram gives your assistants **persistent, queryable memory** — backed by SQLite, designed for simplicity.
|
|
29
|
+
|
|
30
|
+
- **Zero config:** Works out of the box
|
|
31
|
+
- **Fast:** SQLite with proper indexes
|
|
32
|
+
- **Portable:** Single file database
|
|
33
|
+
- **Typed:** Full TypeScript support
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @cartisien/engram
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { Engram } from '@cartisien/engram';
|
|
45
|
+
|
|
46
|
+
const memory = new Engram({
|
|
47
|
+
dbPath: './bot-memory.db' // or ':memory:' for ephemeral
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// In your chat handler
|
|
51
|
+
async function handleChat(sessionId: string, message: string) {
|
|
52
|
+
// 1. Store the user's message
|
|
53
|
+
await memory.remember(sessionId, message, 'user');
|
|
54
|
+
|
|
55
|
+
// 2. Retrieve relevant context
|
|
56
|
+
const context = await memory.recall(sessionId, message, 5);
|
|
57
|
+
|
|
58
|
+
// 3. Build prompt with memory
|
|
59
|
+
const prompt = buildPrompt(context, message);
|
|
60
|
+
|
|
61
|
+
// 4. Get AI response
|
|
62
|
+
const response = await openai.chat.completions.create({ messages: prompt });
|
|
63
|
+
|
|
64
|
+
// 5. Store the response
|
|
65
|
+
await memory.remember(sessionId, response.choices[0].message.content, 'assistant');
|
|
66
|
+
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### `new Engram(config?)`
|
|
74
|
+
|
|
75
|
+
Create a memory instance.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const memory = new Engram({
|
|
79
|
+
dbPath: './memory.db', // Database file path
|
|
80
|
+
maxContextLength: 4000 // Max characters per entry
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `remember(sessionId, content, role?, metadata?)`
|
|
85
|
+
|
|
86
|
+
Store a memory entry.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
await memory.remember('session_abc', 'User loves Thai food', 'user', {
|
|
90
|
+
source: 'preference_extraction'
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `recall(sessionId, query?, limit?, options?)`
|
|
95
|
+
|
|
96
|
+
Retrieve memories for a session.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Recent memories
|
|
100
|
+
const recent = await memory.recall('session_abc', undefined, 10);
|
|
101
|
+
|
|
102
|
+
// Keyword search
|
|
103
|
+
const relevant = await memory.recall('session_abc', 'food preferences', 5);
|
|
104
|
+
|
|
105
|
+
// Filtered
|
|
106
|
+
const userOnly = await memory.recall('session_abc', undefined, 10, { role: 'user' });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `history(sessionId, limit?)`
|
|
110
|
+
|
|
111
|
+
Get chronological conversation history.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const chat = await memory.history('session_abc', 20);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `forget(sessionId, options?)`
|
|
118
|
+
|
|
119
|
+
Delete memories.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Delete all for session
|
|
123
|
+
await memory.forget('session_abc');
|
|
124
|
+
|
|
125
|
+
// Delete specific entry
|
|
126
|
+
await memory.forget('session_abc', { id: 'entry_id' });
|
|
127
|
+
|
|
128
|
+
// Delete old entries
|
|
129
|
+
await memory.forget('session_abc', { before: new Date('2024-01-01') });
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `stats(sessionId)`
|
|
133
|
+
|
|
134
|
+
Get memory statistics.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const stats = await memory.stats('session_abc');
|
|
138
|
+
// { total: 42, byRole: { user: 21, assistant: 21 }, ... }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Philosophy
|
|
142
|
+
|
|
143
|
+
> *"The trace precedes presence."* — Derrida
|
|
144
|
+
|
|
145
|
+
Memory isn't storage. It's the substrate of self.
|
|
146
|
+
|
|
147
|
+
Engram doesn't just persist data. It gives your assistants **continuity** — the ability to learn, reference, and grow across conversations. The Cartesian cogito assumed memory was given. We're making it so.
|
|
148
|
+
|
|
149
|
+
## Roadmap
|
|
150
|
+
|
|
151
|
+
- **v0.1** ✅ SQLite persistence, keyword search
|
|
152
|
+
- **v0.2** 🚧 Semantic search with embeddings
|
|
153
|
+
- **v0.3** 📋 Multi-session context, memory consolidation
|
|
154
|
+
- **v0.4** 📋 Cloud sync, distributed memory
|
|
155
|
+
|
|
156
|
+
## The Trilogy
|
|
157
|
+
|
|
158
|
+
Engram is part of the **Cartisien Memory Suite**:
|
|
159
|
+
|
|
160
|
+
| Package | Purpose |
|
|
161
|
+
|---------|---------|
|
|
162
|
+
| `@cartisien/engram` | **This package** — persistent memory SDK |
|
|
163
|
+
| `@cartisien/extensa` | Vector infrastructure (coming soon) |
|
|
164
|
+
| `@cartisien/cogito` | Identity & state management (coming soon) |
|
|
165
|
+
|
|
166
|
+
*Res cogitans meets res extensa.*
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT © Cartisien Interactive
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
**Built with 🖤 by people who think forgetting is a bug.**
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export interface MemoryEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
sessionId: string;
|
|
4
|
+
content: string;
|
|
5
|
+
role: 'user' | 'assistant' | 'system';
|
|
6
|
+
timestamp: Date;
|
|
7
|
+
metadata?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface RecallOptions {
|
|
10
|
+
limit?: number;
|
|
11
|
+
before?: Date;
|
|
12
|
+
after?: Date;
|
|
13
|
+
role?: 'user' | 'assistant' | 'system';
|
|
14
|
+
}
|
|
15
|
+
export interface EngramConfig {
|
|
16
|
+
dbPath?: string;
|
|
17
|
+
maxContextLength?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Engram - Persistent memory for AI assistants
|
|
21
|
+
*
|
|
22
|
+
* A lightweight, SQLite-backed memory system that gives your AI assistants
|
|
23
|
+
* the ability to remember conversations across sessions.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { Engram } from '@cartisien/engram';
|
|
28
|
+
*
|
|
29
|
+
* const memory = new Engram({ dbPath: './memory.db' });
|
|
30
|
+
*
|
|
31
|
+
* // Store a memory
|
|
32
|
+
* await memory.remember('user_123', 'Jeff loves Triumph motorcycles', 'user');
|
|
33
|
+
*
|
|
34
|
+
* // Retrieve relevant memories
|
|
35
|
+
* const context = await memory.recall('user_123', 'What does Jeff like?', 3);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class Engram {
|
|
39
|
+
private db;
|
|
40
|
+
private maxContextLength;
|
|
41
|
+
private dbPath;
|
|
42
|
+
private initialized;
|
|
43
|
+
constructor(config?: EngramConfig);
|
|
44
|
+
private init;
|
|
45
|
+
/**
|
|
46
|
+
* Store a memory entry
|
|
47
|
+
*/
|
|
48
|
+
remember(sessionId: string, content: string, role?: 'user' | 'assistant' | 'system', metadata?: Record<string, unknown>): Promise<MemoryEntry>;
|
|
49
|
+
/**
|
|
50
|
+
* Recall memories for a session
|
|
51
|
+
*
|
|
52
|
+
* Simple keyword-based retrieval for v0.1.
|
|
53
|
+
* Semantic search coming in v0.2.
|
|
54
|
+
*/
|
|
55
|
+
recall(sessionId: string, query?: string, limit?: number, options?: RecallOptions): Promise<MemoryEntry[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Get recent conversation history for a session
|
|
58
|
+
*/
|
|
59
|
+
history(sessionId: string, limit?: number): Promise<MemoryEntry[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Forget (delete) memories
|
|
62
|
+
*/
|
|
63
|
+
forget(sessionId: string, options?: {
|
|
64
|
+
before?: Date;
|
|
65
|
+
id?: string;
|
|
66
|
+
}): Promise<number>;
|
|
67
|
+
/**
|
|
68
|
+
* Get memory statistics for a session
|
|
69
|
+
*/
|
|
70
|
+
stats(sessionId: string): Promise<{
|
|
71
|
+
total: number;
|
|
72
|
+
byRole: Record<string, number>;
|
|
73
|
+
oldest: Date | null;
|
|
74
|
+
newest: Date | null;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Close the database connection
|
|
78
|
+
*/
|
|
79
|
+
close(): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
export default Engram;
|
|
82
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,GAAE,YAAiB;YAKvB,IAAI;IAuClB;;OAEG;IACG,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,GAAG,WAAW,GAAG,QAAiB,EAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAuCvB;;;;;OAKG;IACG,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC;IAiDzB;;OAEG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,WAAW,EAAE,CAAC;IAIzB;;OAEG;IACG,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,IAAI,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GACvC,OAAO,CAAC,MAAM,CAAC;IAuBlB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;QACpB,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;KACrB,CAAC;IA+BF;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B;AAED,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Engram = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
/**
|
|
6
|
+
* Engram - Persistent memory for AI assistants
|
|
7
|
+
*
|
|
8
|
+
* A lightweight, SQLite-backed memory system that gives your AI assistants
|
|
9
|
+
* the ability to remember conversations across sessions.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Engram } from '@cartisien/engram';
|
|
14
|
+
*
|
|
15
|
+
* const memory = new Engram({ dbPath: './memory.db' });
|
|
16
|
+
*
|
|
17
|
+
* // Store a memory
|
|
18
|
+
* await memory.remember('user_123', 'Jeff loves Triumph motorcycles', 'user');
|
|
19
|
+
*
|
|
20
|
+
* // Retrieve relevant memories
|
|
21
|
+
* const context = await memory.recall('user_123', 'What does Jeff like?', 3);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
class Engram {
|
|
25
|
+
db;
|
|
26
|
+
maxContextLength;
|
|
27
|
+
dbPath;
|
|
28
|
+
initialized = false;
|
|
29
|
+
constructor(config = {}) {
|
|
30
|
+
this.dbPath = config.dbPath || ':memory:';
|
|
31
|
+
this.maxContextLength = config.maxContextLength || 4000;
|
|
32
|
+
}
|
|
33
|
+
async init() {
|
|
34
|
+
if (this.initialized)
|
|
35
|
+
return;
|
|
36
|
+
const sqlite3 = require('sqlite3').verbose();
|
|
37
|
+
const { open } = require('sqlite');
|
|
38
|
+
this.db = await open({
|
|
39
|
+
filename: this.dbPath,
|
|
40
|
+
driver: sqlite3.Database
|
|
41
|
+
});
|
|
42
|
+
// Create memories table
|
|
43
|
+
await this.db.exec(`
|
|
44
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
45
|
+
id TEXT PRIMARY KEY,
|
|
46
|
+
session_id TEXT NOT NULL,
|
|
47
|
+
content TEXT NOT NULL,
|
|
48
|
+
role TEXT CHECK(role IN ('user', 'assistant', 'system')),
|
|
49
|
+
timestamp INTEGER NOT NULL,
|
|
50
|
+
metadata TEXT,
|
|
51
|
+
content_hash TEXT NOT NULL
|
|
52
|
+
);
|
|
53
|
+
`);
|
|
54
|
+
// Create index for fast session lookups
|
|
55
|
+
await this.db.exec(`
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_session_timestamp
|
|
57
|
+
ON memories(session_id, timestamp DESC);
|
|
58
|
+
`);
|
|
59
|
+
// Create index for content search
|
|
60
|
+
await this.db.exec(`
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_content
|
|
62
|
+
ON memories(content);
|
|
63
|
+
`);
|
|
64
|
+
this.initialized = true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Store a memory entry
|
|
68
|
+
*/
|
|
69
|
+
async remember(sessionId, content, role = 'user', metadata) {
|
|
70
|
+
await this.init();
|
|
71
|
+
const id = (0, crypto_1.createHash)('sha256')
|
|
72
|
+
.update(`${sessionId}:${content}:${Date.now()}`)
|
|
73
|
+
.digest('hex')
|
|
74
|
+
.slice(0, 16);
|
|
75
|
+
const contentHash = (0, crypto_1.createHash)('sha256')
|
|
76
|
+
.update(content)
|
|
77
|
+
.digest('hex')
|
|
78
|
+
.slice(0, 16);
|
|
79
|
+
const entry = {
|
|
80
|
+
id,
|
|
81
|
+
sessionId,
|
|
82
|
+
content: content.slice(0, this.maxContextLength),
|
|
83
|
+
role,
|
|
84
|
+
timestamp: new Date(),
|
|
85
|
+
metadata
|
|
86
|
+
};
|
|
87
|
+
await this.db.run(`INSERT INTO memories (id, session_id, content, role, timestamp, metadata, content_hash)
|
|
88
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [
|
|
89
|
+
entry.id,
|
|
90
|
+
entry.sessionId,
|
|
91
|
+
entry.content,
|
|
92
|
+
entry.role,
|
|
93
|
+
entry.timestamp.getTime(),
|
|
94
|
+
metadata ? JSON.stringify(metadata) : null,
|
|
95
|
+
contentHash
|
|
96
|
+
]);
|
|
97
|
+
return entry;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Recall memories for a session
|
|
101
|
+
*
|
|
102
|
+
* Simple keyword-based retrieval for v0.1.
|
|
103
|
+
* Semantic search coming in v0.2.
|
|
104
|
+
*/
|
|
105
|
+
async recall(sessionId, query, limit = 10, options = {}) {
|
|
106
|
+
await this.init();
|
|
107
|
+
let sql = `
|
|
108
|
+
SELECT id, session_id, content, role, timestamp, metadata
|
|
109
|
+
FROM memories
|
|
110
|
+
WHERE session_id = ?
|
|
111
|
+
`;
|
|
112
|
+
const params = [sessionId];
|
|
113
|
+
if (options.role) {
|
|
114
|
+
sql += ` AND role = ?`;
|
|
115
|
+
params.push(options.role);
|
|
116
|
+
}
|
|
117
|
+
if (options.after) {
|
|
118
|
+
sql += ` AND timestamp >= ?`;
|
|
119
|
+
params.push(options.after.getTime());
|
|
120
|
+
}
|
|
121
|
+
if (options.before) {
|
|
122
|
+
sql += ` AND timestamp <= ?`;
|
|
123
|
+
params.push(options.before.getTime());
|
|
124
|
+
}
|
|
125
|
+
// Simple keyword matching if query provided
|
|
126
|
+
if (query && query.trim()) {
|
|
127
|
+
const keywords = query.toLowerCase().split(/\s+/).filter(k => k.length > 2);
|
|
128
|
+
if (keywords.length > 0) {
|
|
129
|
+
sql += ` AND (` + keywords.map(() => `LOWER(content) LIKE ?`).join(' OR ') + `)`;
|
|
130
|
+
params.push(...keywords.map(k => `%${k}%`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
134
|
+
params.push(limit);
|
|
135
|
+
const rows = await this.db.all(sql, params);
|
|
136
|
+
return rows.map((row) => ({
|
|
137
|
+
id: row.id,
|
|
138
|
+
sessionId: row.session_id,
|
|
139
|
+
content: row.content,
|
|
140
|
+
role: row.role,
|
|
141
|
+
timestamp: new Date(row.timestamp),
|
|
142
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : undefined
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get recent conversation history for a session
|
|
147
|
+
*/
|
|
148
|
+
async history(sessionId, limit = 20) {
|
|
149
|
+
return this.recall(sessionId, undefined, limit, {});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Forget (delete) memories
|
|
153
|
+
*/
|
|
154
|
+
async forget(sessionId, options) {
|
|
155
|
+
await this.init();
|
|
156
|
+
if (options?.id) {
|
|
157
|
+
const result = await this.db.run('DELETE FROM memories WHERE session_id = ? AND id = ?', [sessionId, options.id]);
|
|
158
|
+
return result.changes || 0;
|
|
159
|
+
}
|
|
160
|
+
let sql = 'DELETE FROM memories WHERE session_id = ?';
|
|
161
|
+
const params = [sessionId];
|
|
162
|
+
if (options?.before) {
|
|
163
|
+
sql += ' AND timestamp < ?';
|
|
164
|
+
params.push(options.before.getTime());
|
|
165
|
+
}
|
|
166
|
+
const result = await this.db.run(sql, params);
|
|
167
|
+
return result.changes || 0;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get memory statistics for a session
|
|
171
|
+
*/
|
|
172
|
+
async stats(sessionId) {
|
|
173
|
+
await this.init();
|
|
174
|
+
const totalRow = await this.db.get('SELECT COUNT(*) as count FROM memories WHERE session_id = ?', [sessionId]);
|
|
175
|
+
const total = totalRow?.count || 0;
|
|
176
|
+
const roleRows = await this.db.all('SELECT role, COUNT(*) as count FROM memories WHERE session_id = ? GROUP BY role', [sessionId]);
|
|
177
|
+
const byRole = {};
|
|
178
|
+
roleRows.forEach((row) => {
|
|
179
|
+
byRole[row.role] = row.count;
|
|
180
|
+
});
|
|
181
|
+
const range = await this.db.get('SELECT MIN(timestamp) as oldest, MAX(timestamp) as newest FROM memories WHERE session_id = ?', [sessionId]);
|
|
182
|
+
return {
|
|
183
|
+
total,
|
|
184
|
+
byRole,
|
|
185
|
+
oldest: range?.oldest ? new Date(range.oldest) : null,
|
|
186
|
+
newest: range?.newest ? new Date(range.newest) : null
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Close the database connection
|
|
191
|
+
*/
|
|
192
|
+
async close() {
|
|
193
|
+
if (this.db) {
|
|
194
|
+
await this.db.close();
|
|
195
|
+
this.initialized = false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.Engram = Engram;
|
|
200
|
+
exports.default = Engram;
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cartisien/engram",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Persistent memory for AI assistants. SQLite-backed conversational memory with semantic search.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"test": "node --test dist/**/*.test.js",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"memory",
|
|
16
|
+
"openai",
|
|
17
|
+
"assistant",
|
|
18
|
+
"sqlite",
|
|
19
|
+
"embeddings",
|
|
20
|
+
"conversational-memory"
|
|
21
|
+
],
|
|
22
|
+
"author": "Cartisien Interactive",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"sqlite": "^5.1.1",
|
|
26
|
+
"sqlite3": "^5.1.7"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/sqlite3": "^3.1.11",
|
|
30
|
+
"@types/node": "^20.11.0",
|
|
31
|
+
"typescript": "^5.3.3"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/cartisien/engram.git"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|