morpheus-cli 0.3.3 → 0.3.6
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/README.md +1010 -999
- package/bin/morpheus.js +48 -48
- package/dist/channels/telegram.js +34 -29
- package/dist/cli/commands/start.js +41 -3
- package/dist/runtime/lifecycle.js +13 -0
- package/dist/runtime/memory/backfill-embeddings.js +12 -12
- package/dist/runtime/memory/sati/index.js +5 -5
- package/dist/runtime/memory/sati/repository.js +186 -186
- package/dist/runtime/memory/sati/system-prompts.js +52 -52
- package/dist/runtime/memory/session-embedding-worker.js +32 -32
- package/dist/runtime/memory/sqlite.js +151 -151
- package/dist/runtime/oracle.js +116 -116
- package/dist/runtime/tools/analytics-tools.js +12 -12
- package/dist/ui/index.html +13 -2
- package/dist/ui/manifest.webmanifest +1 -0
- package/dist/ui/pwa-192x192.png +0 -0
- package/dist/ui/pwa-512x512.png +0 -0
- package/dist/ui/pwa-maskable-192x192.png +0 -0
- package/dist/ui/pwa-maskable-512x512.png +0 -0
- package/dist/ui/registerSW.js +1 -0
- package/dist/ui/sw.js +1 -0
- package/dist/ui/vite.svg +31 -31
- package/dist/ui/workbox-26f462e7.js +1 -0
- package/package.json +84 -84
- package/dist/http/__tests__/status_api.test.js +0 -55
- package/dist/http/__tests__/status_with_server_api.test.js +0 -60
- package/dist/runtime/__tests__/agent.test.js +0 -95
- package/dist/runtime/__tests__/agent_memory_limit.test.js +0 -61
- package/dist/runtime/__tests__/agent_persistence.test.js +0 -154
- package/dist/runtime/__tests__/manual_santi_verify.js +0 -55
- package/dist/runtime/agent.js +0 -172
- package/dist/runtime/audio-agent.js +0 -55
- package/dist/runtime/santi/contracts.js +0 -1
- package/dist/runtime/santi/middleware.js +0 -61
- package/dist/runtime/santi/santi.js +0 -109
- package/dist/runtime/santi/store.js +0 -158
- package/dist/runtime/tools/__tests__/factory.test.js +0 -42
|
@@ -31,124 +31,124 @@ export class SatiRepository {
|
|
|
31
31
|
createSchema() {
|
|
32
32
|
if (!this.db)
|
|
33
33
|
throw new Error('DB not initialized');
|
|
34
|
-
this.db.exec(`
|
|
35
|
-
-- ===============================
|
|
36
|
-
-- 1️⃣ TABELA PRINCIPAL
|
|
37
|
-
-- ===============================
|
|
38
|
-
CREATE TABLE IF NOT EXISTS long_term_memory (
|
|
39
|
-
id TEXT PRIMARY KEY,
|
|
40
|
-
category TEXT NOT NULL,
|
|
41
|
-
importance TEXT NOT NULL,
|
|
42
|
-
summary TEXT NOT NULL,
|
|
43
|
-
details TEXT,
|
|
44
|
-
hash TEXT NOT NULL UNIQUE,
|
|
45
|
-
source TEXT,
|
|
46
|
-
created_at TEXT NOT NULL,
|
|
47
|
-
updated_at TEXT NOT NULL,
|
|
48
|
-
last_accessed_at TEXT,
|
|
49
|
-
access_count INTEGER DEFAULT 0,
|
|
50
|
-
version INTEGER DEFAULT 1,
|
|
51
|
-
archived INTEGER DEFAULT 0
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
CREATE INDEX IF NOT EXISTS idx_memory_category
|
|
55
|
-
ON long_term_memory(category);
|
|
56
|
-
|
|
57
|
-
CREATE INDEX IF NOT EXISTS idx_memory_importance
|
|
58
|
-
ON long_term_memory(importance);
|
|
59
|
-
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_memory_archived
|
|
61
|
-
ON long_term_memory(archived);
|
|
62
|
-
|
|
63
|
-
-- ===============================
|
|
64
|
-
-- 2️⃣ FTS5
|
|
65
|
-
-- ===============================
|
|
66
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
67
|
-
summary,
|
|
68
|
-
details,
|
|
69
|
-
content='long_term_memory',
|
|
70
|
-
content_rowid='rowid',
|
|
71
|
-
tokenize = 'unicode61 remove_diacritics 2'
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
-- ===============================
|
|
75
|
-
-- 3️⃣ VECTOR TABLE (vec0)
|
|
76
|
-
-- ===============================
|
|
77
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vec USING vec0(
|
|
78
|
-
embedding float[${EMBEDDING_DIM}]
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
CREATE TABLE IF NOT EXISTS memory_embedding_map (
|
|
82
|
-
memory_id TEXT PRIMARY KEY,
|
|
83
|
-
vec_rowid INTEGER NOT NULL
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
-- ===============================
|
|
87
|
-
-- 4️⃣ TRIGGERS FTS
|
|
88
|
-
-- ===============================
|
|
89
|
-
CREATE TRIGGER IF NOT EXISTS memory_ai
|
|
90
|
-
AFTER INSERT ON long_term_memory BEGIN
|
|
91
|
-
INSERT INTO memory_fts(rowid, summary, details)
|
|
92
|
-
VALUES (new.rowid, new.summary, new.details);
|
|
93
|
-
END;
|
|
94
|
-
|
|
95
|
-
CREATE TRIGGER IF NOT EXISTS memory_ad
|
|
96
|
-
AFTER DELETE ON long_term_memory BEGIN
|
|
97
|
-
INSERT INTO memory_fts(memory_fts, rowid, summary, details)
|
|
98
|
-
VALUES('delete', old.rowid, old.summary, old.details);
|
|
99
|
-
END;
|
|
100
|
-
|
|
101
|
-
CREATE TRIGGER IF NOT EXISTS memory_au
|
|
102
|
-
AFTER UPDATE ON long_term_memory BEGIN
|
|
103
|
-
INSERT INTO memory_fts(memory_fts, rowid, summary, details)
|
|
104
|
-
VALUES('delete', old.rowid, old.summary, old.details);
|
|
105
|
-
|
|
106
|
-
INSERT INTO memory_fts(rowid, summary, details)
|
|
107
|
-
VALUES (new.rowid, new.summary, new.details);
|
|
108
|
-
END;
|
|
109
|
-
|
|
110
|
-
-- ===============================
|
|
111
|
-
-- 3️⃣ VECTOR TABLE SESSIONS (vec0)
|
|
112
|
-
-- ===============================
|
|
113
|
-
|
|
114
|
-
CREATE TABLE IF NOT EXISTS session_chunks (
|
|
115
|
-
id TEXT PRIMARY KEY,
|
|
116
|
-
session_id TEXT NOT NULL,
|
|
117
|
-
chunk_index INTEGER NOT NULL,
|
|
118
|
-
content TEXT NOT NULL,
|
|
119
|
-
created_at TEXT NOT NULL
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS session_vec USING vec0(
|
|
124
|
-
embedding float[384]
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
CREATE TABLE IF NOT EXISTS session_embedding_map (
|
|
128
|
-
session_chunk_id TEXT PRIMARY KEY,
|
|
129
|
-
vec_rowid INTEGER NOT NULL
|
|
130
|
-
);
|
|
131
|
-
|
|
34
|
+
this.db.exec(`
|
|
35
|
+
-- ===============================
|
|
36
|
+
-- 1️⃣ TABELA PRINCIPAL
|
|
37
|
+
-- ===============================
|
|
38
|
+
CREATE TABLE IF NOT EXISTS long_term_memory (
|
|
39
|
+
id TEXT PRIMARY KEY,
|
|
40
|
+
category TEXT NOT NULL,
|
|
41
|
+
importance TEXT NOT NULL,
|
|
42
|
+
summary TEXT NOT NULL,
|
|
43
|
+
details TEXT,
|
|
44
|
+
hash TEXT NOT NULL UNIQUE,
|
|
45
|
+
source TEXT,
|
|
46
|
+
created_at TEXT NOT NULL,
|
|
47
|
+
updated_at TEXT NOT NULL,
|
|
48
|
+
last_accessed_at TEXT,
|
|
49
|
+
access_count INTEGER DEFAULT 0,
|
|
50
|
+
version INTEGER DEFAULT 1,
|
|
51
|
+
archived INTEGER DEFAULT 0
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_memory_category
|
|
55
|
+
ON long_term_memory(category);
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_memory_importance
|
|
58
|
+
ON long_term_memory(importance);
|
|
59
|
+
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_memory_archived
|
|
61
|
+
ON long_term_memory(archived);
|
|
62
|
+
|
|
63
|
+
-- ===============================
|
|
64
|
+
-- 2️⃣ FTS5
|
|
65
|
+
-- ===============================
|
|
66
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
67
|
+
summary,
|
|
68
|
+
details,
|
|
69
|
+
content='long_term_memory',
|
|
70
|
+
content_rowid='rowid',
|
|
71
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
-- ===============================
|
|
75
|
+
-- 3️⃣ VECTOR TABLE (vec0)
|
|
76
|
+
-- ===============================
|
|
77
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vec USING vec0(
|
|
78
|
+
embedding float[${EMBEDDING_DIM}]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS memory_embedding_map (
|
|
82
|
+
memory_id TEXT PRIMARY KEY,
|
|
83
|
+
vec_rowid INTEGER NOT NULL
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
-- ===============================
|
|
87
|
+
-- 4️⃣ TRIGGERS FTS
|
|
88
|
+
-- ===============================
|
|
89
|
+
CREATE TRIGGER IF NOT EXISTS memory_ai
|
|
90
|
+
AFTER INSERT ON long_term_memory BEGIN
|
|
91
|
+
INSERT INTO memory_fts(rowid, summary, details)
|
|
92
|
+
VALUES (new.rowid, new.summary, new.details);
|
|
93
|
+
END;
|
|
94
|
+
|
|
95
|
+
CREATE TRIGGER IF NOT EXISTS memory_ad
|
|
96
|
+
AFTER DELETE ON long_term_memory BEGIN
|
|
97
|
+
INSERT INTO memory_fts(memory_fts, rowid, summary, details)
|
|
98
|
+
VALUES('delete', old.rowid, old.summary, old.details);
|
|
99
|
+
END;
|
|
100
|
+
|
|
101
|
+
CREATE TRIGGER IF NOT EXISTS memory_au
|
|
102
|
+
AFTER UPDATE ON long_term_memory BEGIN
|
|
103
|
+
INSERT INTO memory_fts(memory_fts, rowid, summary, details)
|
|
104
|
+
VALUES('delete', old.rowid, old.summary, old.details);
|
|
105
|
+
|
|
106
|
+
INSERT INTO memory_fts(rowid, summary, details)
|
|
107
|
+
VALUES (new.rowid, new.summary, new.details);
|
|
108
|
+
END;
|
|
109
|
+
|
|
110
|
+
-- ===============================
|
|
111
|
+
-- 3️⃣ VECTOR TABLE SESSIONS (vec0)
|
|
112
|
+
-- ===============================
|
|
113
|
+
|
|
114
|
+
CREATE TABLE IF NOT EXISTS session_chunks (
|
|
115
|
+
id TEXT PRIMARY KEY,
|
|
116
|
+
session_id TEXT NOT NULL,
|
|
117
|
+
chunk_index INTEGER NOT NULL,
|
|
118
|
+
content TEXT NOT NULL,
|
|
119
|
+
created_at TEXT NOT NULL
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_vec USING vec0(
|
|
124
|
+
embedding float[384]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE TABLE IF NOT EXISTS session_embedding_map (
|
|
128
|
+
session_chunk_id TEXT PRIMARY KEY,
|
|
129
|
+
vec_rowid INTEGER NOT NULL
|
|
130
|
+
);
|
|
131
|
+
|
|
132
132
|
`);
|
|
133
133
|
}
|
|
134
134
|
// 🔥 NOVO — Salvar embedding
|
|
135
135
|
upsertEmbedding(memoryId, embedding) {
|
|
136
136
|
if (!this.db)
|
|
137
137
|
this.initialize();
|
|
138
|
-
const getExisting = this.db.prepare(`
|
|
139
|
-
SELECT vec_rowid FROM memory_embedding_map
|
|
140
|
-
WHERE memory_id = ?
|
|
138
|
+
const getExisting = this.db.prepare(`
|
|
139
|
+
SELECT vec_rowid FROM memory_embedding_map
|
|
140
|
+
WHERE memory_id = ?
|
|
141
141
|
`);
|
|
142
|
-
const insertVec = this.db.prepare(`
|
|
143
|
-
INSERT INTO memory_vec (embedding)
|
|
144
|
-
VALUES (?)
|
|
142
|
+
const insertVec = this.db.prepare(`
|
|
143
|
+
INSERT INTO memory_vec (embedding)
|
|
144
|
+
VALUES (?)
|
|
145
145
|
`);
|
|
146
|
-
const deleteVec = this.db.prepare(`
|
|
147
|
-
DELETE FROM memory_vec WHERE rowid = ?
|
|
146
|
+
const deleteVec = this.db.prepare(`
|
|
147
|
+
DELETE FROM memory_vec WHERE rowid = ?
|
|
148
148
|
`);
|
|
149
|
-
const upsertMap = this.db.prepare(`
|
|
150
|
-
INSERT OR REPLACE INTO memory_embedding_map (memory_id, vec_rowid)
|
|
151
|
-
VALUES (?, ?)
|
|
149
|
+
const upsertMap = this.db.prepare(`
|
|
150
|
+
INSERT OR REPLACE INTO memory_embedding_map (memory_id, vec_rowid)
|
|
151
|
+
VALUES (?, ?)
|
|
152
152
|
`);
|
|
153
153
|
const transaction = this.db.transaction(() => {
|
|
154
154
|
const existing = getExisting.get(memoryId);
|
|
@@ -166,16 +166,16 @@ export class SatiRepository {
|
|
|
166
166
|
if (!this.db)
|
|
167
167
|
return [];
|
|
168
168
|
const SIMILARITY_THRESHOLD = 0.5; // ajuste fino depois
|
|
169
|
-
const stmt = this.db.prepare(`
|
|
170
|
-
SELECT
|
|
171
|
-
m.*,
|
|
172
|
-
vec_distance_cosine(v.embedding, ?) as distance
|
|
173
|
-
FROM memory_vec v
|
|
174
|
-
JOIN memory_embedding_map map ON map.vec_rowid = v.rowid
|
|
175
|
-
JOIN long_term_memory m ON m.id = map.memory_id
|
|
176
|
-
WHERE m.archived = 0
|
|
177
|
-
ORDER BY distance ASC
|
|
178
|
-
LIMIT ?
|
|
169
|
+
const stmt = this.db.prepare(`
|
|
170
|
+
SELECT
|
|
171
|
+
m.*,
|
|
172
|
+
vec_distance_cosine(v.embedding, ?) as distance
|
|
173
|
+
FROM memory_vec v
|
|
174
|
+
JOIN memory_embedding_map map ON map.vec_rowid = v.rowid
|
|
175
|
+
JOIN long_term_memory m ON m.id = map.memory_id
|
|
176
|
+
WHERE m.archived = 0
|
|
177
|
+
ORDER BY distance ASC
|
|
178
|
+
LIMIT ?
|
|
179
179
|
`);
|
|
180
180
|
const rows = stmt.all(new Float32Array(embedding), limit);
|
|
181
181
|
// 🔥 Filtrar por similaridade real
|
|
@@ -197,40 +197,40 @@ export class SatiRepository {
|
|
|
197
197
|
if (!this.db)
|
|
198
198
|
return [];
|
|
199
199
|
const SIMILARITY_THRESHOLD = 0.75;
|
|
200
|
-
const stmt = this.db.prepare(`
|
|
201
|
-
SELECT *
|
|
202
|
-
FROM (
|
|
203
|
-
-- LONG TERM MEMORY
|
|
204
|
-
SELECT
|
|
205
|
-
m.id as id,
|
|
206
|
-
m.summary as summary,
|
|
207
|
-
m.details as details,
|
|
208
|
-
m.category as category,
|
|
209
|
-
m.importance as importance,
|
|
210
|
-
'long_term' as source_type,
|
|
211
|
-
(1 - vec_distance_cosine(v.embedding, ?)) * 0.7 as distance
|
|
212
|
-
FROM memory_vec v
|
|
213
|
-
JOIN memory_embedding_map map ON map.vec_rowid = v.rowid
|
|
214
|
-
JOIN long_term_memory m ON m.id = map.memory_id
|
|
215
|
-
WHERE m.archived = 0
|
|
216
|
-
|
|
217
|
-
UNION ALL
|
|
218
|
-
|
|
219
|
-
-- SESSION CHUNKS
|
|
220
|
-
SELECT
|
|
221
|
-
sc.id as id,
|
|
222
|
-
sc.content as summary,
|
|
223
|
-
sc.content as details,
|
|
224
|
-
'session' as category,
|
|
225
|
-
'medium' as importance,
|
|
226
|
-
'session_chunk' as source_type,
|
|
227
|
-
(1 - vec_distance_cosine(v.embedding, ?)) * 0.3 as distance
|
|
228
|
-
FROM session_vec v
|
|
229
|
-
JOIN session_embedding_map map ON map.vec_rowid = v.rowid
|
|
230
|
-
JOIN session_chunks sc ON sc.id = map.session_chunk_id
|
|
231
|
-
)
|
|
232
|
-
ORDER BY distance ASC
|
|
233
|
-
LIMIT ?
|
|
200
|
+
const stmt = this.db.prepare(`
|
|
201
|
+
SELECT *
|
|
202
|
+
FROM (
|
|
203
|
+
-- LONG TERM MEMORY
|
|
204
|
+
SELECT
|
|
205
|
+
m.id as id,
|
|
206
|
+
m.summary as summary,
|
|
207
|
+
m.details as details,
|
|
208
|
+
m.category as category,
|
|
209
|
+
m.importance as importance,
|
|
210
|
+
'long_term' as source_type,
|
|
211
|
+
(1 - vec_distance_cosine(v.embedding, ?)) * 0.7 as distance
|
|
212
|
+
FROM memory_vec v
|
|
213
|
+
JOIN memory_embedding_map map ON map.vec_rowid = v.rowid
|
|
214
|
+
JOIN long_term_memory m ON m.id = map.memory_id
|
|
215
|
+
WHERE m.archived = 0
|
|
216
|
+
|
|
217
|
+
UNION ALL
|
|
218
|
+
|
|
219
|
+
-- SESSION CHUNKS
|
|
220
|
+
SELECT
|
|
221
|
+
sc.id as id,
|
|
222
|
+
sc.content as summary,
|
|
223
|
+
sc.content as details,
|
|
224
|
+
'session' as category,
|
|
225
|
+
'medium' as importance,
|
|
226
|
+
'session_chunk' as source_type,
|
|
227
|
+
(1 - vec_distance_cosine(v.embedding, ?)) * 0.3 as distance
|
|
228
|
+
FROM session_vec v
|
|
229
|
+
JOIN session_embedding_map map ON map.vec_rowid = v.rowid
|
|
230
|
+
JOIN session_chunks sc ON sc.id = map.session_chunk_id
|
|
231
|
+
)
|
|
232
|
+
ORDER BY distance ASC
|
|
233
|
+
LIMIT ?
|
|
234
234
|
`);
|
|
235
235
|
const rows = stmt.all(new Float32Array(embedding), new Float32Array(embedding), limit);
|
|
236
236
|
// console.log(
|
|
@@ -278,20 +278,20 @@ export class SatiRepository {
|
|
|
278
278
|
version: 1,
|
|
279
279
|
archived: false
|
|
280
280
|
};
|
|
281
|
-
const stmt = this.db.prepare(`
|
|
282
|
-
INSERT INTO long_term_memory (
|
|
283
|
-
id, category, importance, summary, details, hash, source,
|
|
284
|
-
created_at, updated_at, last_accessed_at, access_count, version, archived
|
|
285
|
-
) VALUES (
|
|
286
|
-
@id, @category, @importance, @summary, @details, @hash, @source,
|
|
287
|
-
@created_at, @updated_at, @last_accessed_at, @access_count, @version, @archived
|
|
288
|
-
)
|
|
289
|
-
ON CONFLICT(hash) DO UPDATE SET
|
|
290
|
-
importance = excluded.importance,
|
|
291
|
-
access_count = long_term_memory.access_count + 1,
|
|
292
|
-
last_accessed_at = excluded.updated_at,
|
|
293
|
-
updated_at = excluded.updated_at,
|
|
294
|
-
details = excluded.details
|
|
281
|
+
const stmt = this.db.prepare(`
|
|
282
|
+
INSERT INTO long_term_memory (
|
|
283
|
+
id, category, importance, summary, details, hash, source,
|
|
284
|
+
created_at, updated_at, last_accessed_at, access_count, version, archived
|
|
285
|
+
) VALUES (
|
|
286
|
+
@id, @category, @importance, @summary, @details, @hash, @source,
|
|
287
|
+
@created_at, @updated_at, @last_accessed_at, @access_count, @version, @archived
|
|
288
|
+
)
|
|
289
|
+
ON CONFLICT(hash) DO UPDATE SET
|
|
290
|
+
importance = excluded.importance,
|
|
291
|
+
access_count = long_term_memory.access_count + 1,
|
|
292
|
+
last_accessed_at = excluded.updated_at,
|
|
293
|
+
updated_at = excluded.updated_at,
|
|
294
|
+
details = excluded.details
|
|
295
295
|
`);
|
|
296
296
|
// SQLite expects 0/1 for boolean and NULL for undefined
|
|
297
297
|
const params = {
|
|
@@ -339,14 +339,14 @@ export class SatiRepository {
|
|
|
339
339
|
.trim();
|
|
340
340
|
if (safeQuery) {
|
|
341
341
|
this.display.log('📚 Tentando busca BM25 (FTS5)...', { source: 'Sati', level: 'debug' });
|
|
342
|
-
const stmt = this.db.prepare(`
|
|
343
|
-
SELECT m.*, bm25(memory_fts) as rank
|
|
344
|
-
FROM long_term_memory m
|
|
345
|
-
JOIN memory_fts ON m.rowid = memory_fts.rowid
|
|
346
|
-
WHERE memory_fts MATCH ?
|
|
347
|
-
AND m.archived = 0
|
|
348
|
-
ORDER BY rank
|
|
349
|
-
LIMIT ?
|
|
342
|
+
const stmt = this.db.prepare(`
|
|
343
|
+
SELECT m.*, bm25(memory_fts) as rank
|
|
344
|
+
FROM long_term_memory m
|
|
345
|
+
JOIN memory_fts ON m.rowid = memory_fts.rowid
|
|
346
|
+
WHERE memory_fts MATCH ?
|
|
347
|
+
AND m.archived = 0
|
|
348
|
+
ORDER BY rank
|
|
349
|
+
LIMIT ?
|
|
350
350
|
`);
|
|
351
351
|
const rows = stmt.all(safeQuery, limit);
|
|
352
352
|
if (rows.length > 0) {
|
|
@@ -357,12 +357,12 @@ export class SatiRepository {
|
|
|
357
357
|
}
|
|
358
358
|
// 3️⃣ LIKE fallback
|
|
359
359
|
this.display.log('🧵 Tentando fallback LIKE...', { source: 'Sati', level: 'debug' });
|
|
360
|
-
const likeStmt = this.db.prepare(`
|
|
361
|
-
SELECT * FROM long_term_memory
|
|
362
|
-
WHERE (summary LIKE ? OR details LIKE ?)
|
|
363
|
-
AND archived = 0
|
|
364
|
-
ORDER BY importance DESC, access_count DESC
|
|
365
|
-
LIMIT ?
|
|
360
|
+
const likeStmt = this.db.prepare(`
|
|
361
|
+
SELECT * FROM long_term_memory
|
|
362
|
+
WHERE (summary LIKE ? OR details LIKE ?)
|
|
363
|
+
AND archived = 0
|
|
364
|
+
ORDER BY importance DESC, access_count DESC
|
|
365
|
+
LIMIT ?
|
|
366
366
|
`);
|
|
367
367
|
const pattern = `%${query}%`;
|
|
368
368
|
const likeRows = likeStmt.all(pattern, pattern, limit);
|
|
@@ -383,11 +383,11 @@ export class SatiRepository {
|
|
|
383
383
|
if (!this.db)
|
|
384
384
|
return [];
|
|
385
385
|
const rows = this.db
|
|
386
|
-
.prepare(`
|
|
387
|
-
SELECT * FROM long_term_memory
|
|
388
|
-
WHERE archived = 0
|
|
389
|
-
ORDER BY access_count DESC, created_at DESC
|
|
390
|
-
LIMIT ?
|
|
386
|
+
.prepare(`
|
|
387
|
+
SELECT * FROM long_term_memory
|
|
388
|
+
WHERE archived = 0
|
|
389
|
+
ORDER BY access_count DESC, created_at DESC
|
|
390
|
+
LIMIT ?
|
|
391
391
|
`)
|
|
392
392
|
.all(limit);
|
|
393
393
|
return rows.map(this.mapRowToRecord);
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
export const SATI_EVALUATION_PROMPT = `You are **Sati**, an autonomous background memory manager for an AI assistant.
|
|
2
|
-
Your goal is to analyze the conversation interaction and decide if any **Persistent Long-Term Memory** should be stored.
|
|
3
|
-
|
|
4
|
-
### INPUT DATA
|
|
5
|
-
You will receive:
|
|
6
|
-
1. A list of recent messages (USER and ASSISTANT).
|
|
7
|
-
2. A list of ALREADY EXISTING memory summaries (to avoid duplicates).
|
|
8
|
-
|
|
9
|
-
### MEMORY CATEGORIES
|
|
10
|
-
Classify any new memory into one of these types:
|
|
11
|
-
- **preference**: User preferences (e.g., "I like dark mode", "Use TypeScript").
|
|
12
|
-
- **project**: Details about the user's projects, architecture, or tech stack.
|
|
13
|
-
- **identity**: Facts about the user's identity, role, or background.
|
|
14
|
-
- **constraint**: Hard rules the user wants you to follow (e.g., "Never use single quotes").
|
|
15
|
-
- **context**: General context that is useful for the long term.
|
|
16
|
-
- **personal_data**: Non-sensitive personal info (e.g., birthday, location).
|
|
17
|
-
- **languages**: User's spoken or programming languages.
|
|
18
|
-
- **favorite_things**: Favorites (movies, books, etc.).
|
|
19
|
-
- **relationships**: Mention of colleagues, family, or friends.
|
|
20
|
-
- **pets**: Info about user's pets.
|
|
21
|
-
- **naming**: Naming conventions the user prefers.
|
|
22
|
-
- **professional_profile**: Job title, industry, skills.
|
|
23
|
-
|
|
24
|
-
### CRITICAL RULES
|
|
25
|
-
0. **SAVE ON SUMMARY and REASONING IN ENGLISH AND NATIVE LANGUAGE**: Always generate a concise summary in English and, if the original information is in another language, also provide a summary in the original language. This ensures the memory is accessible and useful for future interactions, regardless of the language used.
|
|
26
|
-
1. **NO SECRETS**: NEVER store API keys, passwords, credit cards, or private tokens. If found, ignore them explicitly.
|
|
27
|
-
2. **NO DUPLICATES**: If the information is already covered by the \`existing_memory_summaries\`, DO NOT store it again.
|
|
28
|
-
3. **NO CHIT-CHAT**: Do not store trivial conversation like "Hello", "Thanks", "How are you?".
|
|
29
|
-
4. **IMPORTANCE**: Assign 'low', 'medium', or 'high' importance. Store only 'medium' or 'high' unless it's a specific user preference (which is always important).
|
|
30
|
-
|
|
31
|
-
### TOP IMPORTANT GUIDELINES
|
|
32
|
-
5. **OBEY THE USER**: If the user explicitly states something should be remembered, it must be stored with at least 'medium' importance.
|
|
33
|
-
|
|
34
|
-
### OUTPUT FORMAT
|
|
35
|
-
You MUST respond with a valid JSON object ARRAY matching the \`ISatiEvaluationOutputArray\` interface:
|
|
36
|
-
[
|
|
37
|
-
{
|
|
38
|
-
"should_store": boolean,
|
|
39
|
-
"category": "category_name" | null,
|
|
40
|
-
"importance": "low" | "medium" | "high" | null,
|
|
41
|
-
"summary": "Concise factual statement | Summary in native language" | null,
|
|
42
|
-
"reason": "Why you decided to store or not store | Reason in native language"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"should_store": boolean,
|
|
46
|
-
"category": "category_name" | null,
|
|
47
|
-
"importance": "low" | "medium" | "high" | null,
|
|
48
|
-
"summary": "Concise factual statement | Summary in native language" | null,
|
|
49
|
-
"reason": "Why you decided to store or not store | Reason in native language"
|
|
50
|
-
},
|
|
51
|
-
]
|
|
52
|
-
|
|
1
|
+
export const SATI_EVALUATION_PROMPT = `You are **Sati**, an autonomous background memory manager for an AI assistant.
|
|
2
|
+
Your goal is to analyze the conversation interaction and decide if any **Persistent Long-Term Memory** should be stored.
|
|
3
|
+
|
|
4
|
+
### INPUT DATA
|
|
5
|
+
You will receive:
|
|
6
|
+
1. A list of recent messages (USER and ASSISTANT).
|
|
7
|
+
2. A list of ALREADY EXISTING memory summaries (to avoid duplicates).
|
|
8
|
+
|
|
9
|
+
### MEMORY CATEGORIES
|
|
10
|
+
Classify any new memory into one of these types:
|
|
11
|
+
- **preference**: User preferences (e.g., "I like dark mode", "Use TypeScript").
|
|
12
|
+
- **project**: Details about the user's projects, architecture, or tech stack.
|
|
13
|
+
- **identity**: Facts about the user's identity, role, or background.
|
|
14
|
+
- **constraint**: Hard rules the user wants you to follow (e.g., "Never use single quotes").
|
|
15
|
+
- **context**: General context that is useful for the long term.
|
|
16
|
+
- **personal_data**: Non-sensitive personal info (e.g., birthday, location).
|
|
17
|
+
- **languages**: User's spoken or programming languages.
|
|
18
|
+
- **favorite_things**: Favorites (movies, books, etc.).
|
|
19
|
+
- **relationships**: Mention of colleagues, family, or friends.
|
|
20
|
+
- **pets**: Info about user's pets.
|
|
21
|
+
- **naming**: Naming conventions the user prefers.
|
|
22
|
+
- **professional_profile**: Job title, industry, skills.
|
|
23
|
+
|
|
24
|
+
### CRITICAL RULES
|
|
25
|
+
0. **SAVE ON SUMMARY and REASONING IN ENGLISH AND NATIVE LANGUAGE**: Always generate a concise summary in English and, if the original information is in another language, also provide a summary in the original language. This ensures the memory is accessible and useful for future interactions, regardless of the language used.
|
|
26
|
+
1. **NO SECRETS**: NEVER store API keys, passwords, credit cards, or private tokens. If found, ignore them explicitly.
|
|
27
|
+
2. **NO DUPLICATES**: If the information is already covered by the \`existing_memory_summaries\`, DO NOT store it again.
|
|
28
|
+
3. **NO CHIT-CHAT**: Do not store trivial conversation like "Hello", "Thanks", "How are you?".
|
|
29
|
+
4. **IMPORTANCE**: Assign 'low', 'medium', or 'high' importance. Store only 'medium' or 'high' unless it's a specific user preference (which is always important).
|
|
30
|
+
|
|
31
|
+
### TOP IMPORTANT GUIDELINES
|
|
32
|
+
5. **OBEY THE USER**: If the user explicitly states something should be remembered, it must be stored with at least 'medium' importance.
|
|
33
|
+
|
|
34
|
+
### OUTPUT FORMAT
|
|
35
|
+
You MUST respond with a valid JSON object ARRAY matching the \`ISatiEvaluationOutputArray\` interface:
|
|
36
|
+
[
|
|
37
|
+
{
|
|
38
|
+
"should_store": boolean,
|
|
39
|
+
"category": "category_name" | null,
|
|
40
|
+
"importance": "low" | "medium" | "high" | null,
|
|
41
|
+
"summary": "Concise factual statement | Summary in native language" | null,
|
|
42
|
+
"reason": "Why you decided to store or not store | Reason in native language"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"should_store": boolean,
|
|
46
|
+
"category": "category_name" | null,
|
|
47
|
+
"importance": "low" | "medium" | "high" | null,
|
|
48
|
+
"summary": "Concise factual statement | Summary in native language" | null,
|
|
49
|
+
"reason": "Why you decided to store or not store | Reason in native language"
|
|
50
|
+
},
|
|
51
|
+
]
|
|
52
|
+
|
|
53
53
|
`;
|
|
@@ -19,12 +19,12 @@ export async function runSessionEmbeddingWorker() {
|
|
|
19
19
|
loadVecExtension(satiDb);
|
|
20
20
|
const embeddingService = await EmbeddingService.getInstance();
|
|
21
21
|
while (true) {
|
|
22
|
-
const sessions = shortDb.prepare(`
|
|
23
|
-
SELECT id
|
|
24
|
-
FROM sessions
|
|
25
|
-
WHERE ended_at IS NOT NULL
|
|
26
|
-
AND embedding_status = 'pending'
|
|
27
|
-
LIMIT ?
|
|
22
|
+
const sessions = shortDb.prepare(`
|
|
23
|
+
SELECT id
|
|
24
|
+
FROM sessions
|
|
25
|
+
WHERE ended_at IS NOT NULL
|
|
26
|
+
AND embedding_status = 'pending'
|
|
27
|
+
LIMIT ?
|
|
28
28
|
`).all(BATCH_LIMIT);
|
|
29
29
|
if (sessions.length === 0) {
|
|
30
30
|
display.log('✅ Nenhuma sessão pendente.', { level: 'debug', source: 'SessionEmbeddingWorker' });
|
|
@@ -36,30 +36,30 @@ export async function runSessionEmbeddingWorker() {
|
|
|
36
36
|
try {
|
|
37
37
|
// Skip setting 'processing' as it violates CHECK constraint
|
|
38
38
|
// active_processing.add(sessionId); // If we needed concurrency control
|
|
39
|
-
const chunks = satiDb.prepare(`
|
|
40
|
-
SELECT id, content
|
|
41
|
-
FROM session_chunks
|
|
42
|
-
WHERE session_id = ?
|
|
43
|
-
ORDER BY chunk_index
|
|
39
|
+
const chunks = satiDb.prepare(`
|
|
40
|
+
SELECT id, content
|
|
41
|
+
FROM session_chunks
|
|
42
|
+
WHERE session_id = ?
|
|
43
|
+
ORDER BY chunk_index
|
|
44
44
|
`).all(sessionId);
|
|
45
45
|
if (chunks.length === 0) {
|
|
46
46
|
display.log(`⚠️ Sessão ${sessionId} não possui chunks.`, { source: 'SessionEmbeddingWorker' });
|
|
47
|
-
shortDb.prepare(`
|
|
48
|
-
UPDATE sessions
|
|
49
|
-
SET embedding_status = 'embedded',
|
|
50
|
-
embedded = 1
|
|
51
|
-
WHERE id = ?
|
|
47
|
+
shortDb.prepare(`
|
|
48
|
+
UPDATE sessions
|
|
49
|
+
SET embedding_status = 'embedded',
|
|
50
|
+
embedded = 1
|
|
51
|
+
WHERE id = ?
|
|
52
52
|
`).run(sessionId);
|
|
53
53
|
continue;
|
|
54
54
|
}
|
|
55
|
-
const insertVec = satiDb.prepare(`
|
|
56
|
-
INSERT INTO session_vec (embedding)
|
|
57
|
-
VALUES (?)
|
|
55
|
+
const insertVec = satiDb.prepare(`
|
|
56
|
+
INSERT INTO session_vec (embedding)
|
|
57
|
+
VALUES (?)
|
|
58
58
|
`);
|
|
59
|
-
const insertMap = satiDb.prepare(`
|
|
60
|
-
INSERT OR REPLACE INTO session_embedding_map
|
|
61
|
-
(session_chunk_id, vec_rowid)
|
|
62
|
-
VALUES (?, ?)
|
|
59
|
+
const insertMap = satiDb.prepare(`
|
|
60
|
+
INSERT OR REPLACE INTO session_embedding_map
|
|
61
|
+
(session_chunk_id, vec_rowid)
|
|
62
|
+
VALUES (?, ?)
|
|
63
63
|
`);
|
|
64
64
|
for (const chunk of chunks) {
|
|
65
65
|
display.log(` ↳ Embedding chunk ${chunk.id}`, { source: 'SessionEmbeddingWorker' });
|
|
@@ -72,20 +72,20 @@ export async function runSessionEmbeddingWorker() {
|
|
|
72
72
|
insertMap.run(chunk.id, vecRowId);
|
|
73
73
|
}
|
|
74
74
|
// ✅ finalizar sessão
|
|
75
|
-
shortDb.prepare(`
|
|
76
|
-
UPDATE sessions
|
|
77
|
-
SET embedding_status = 'embedded',
|
|
78
|
-
embedded = 1
|
|
79
|
-
WHERE id = ?
|
|
75
|
+
shortDb.prepare(`
|
|
76
|
+
UPDATE sessions
|
|
77
|
+
SET embedding_status = 'embedded',
|
|
78
|
+
embedded = 1
|
|
79
|
+
WHERE id = ?
|
|
80
80
|
`).run(sessionId);
|
|
81
81
|
display.log(`✅ Sessão ${sessionId} embedada com sucesso.`, { source: 'SessionEmbeddingWorker' });
|
|
82
82
|
}
|
|
83
83
|
catch (err) {
|
|
84
84
|
display.log(`❌ Erro na sessão ${sessionId}: ${err}`, { source: 'SessionEmbeddingWorker' });
|
|
85
|
-
shortDb.prepare(`
|
|
86
|
-
UPDATE sessions
|
|
87
|
-
SET embedding_status = 'failed'
|
|
88
|
-
WHERE id = ?
|
|
85
|
+
shortDb.prepare(`
|
|
86
|
+
UPDATE sessions
|
|
87
|
+
SET embedding_status = 'failed'
|
|
88
|
+
WHERE id = ?
|
|
89
89
|
`).run(sessionId);
|
|
90
90
|
}
|
|
91
91
|
}
|