@prometheus-ai/memory 0.5.3 → 0.5.8
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/CHANGELOG.md +15 -0
- package/README.md +4 -4
- package/dist/types/config.d.ts +13 -2
- package/dist/types/core/beam/store.d.ts +20 -0
- package/dist/types/core/embeddings.d.ts +2 -1
- package/dist/types/core/extraction/client.d.ts +11 -7
- package/dist/types/core/extraction.d.ts +2 -1
- package/dist/types/core/fastembed-runtime.d.ts +4 -0
- package/dist/types/core/index.d.ts +1 -0
- package/dist/types/core/llm-backends.d.ts +2 -0
- package/dist/types/core/local-llm.d.ts +8 -3
- package/dist/types/core/memory.d.ts +12 -3
- package/dist/types/core/query-cache.d.ts +1 -2
- package/dist/types/core/runtime-options.d.ts +10 -5
- package/dist/types/core/shmr.d.ts +11 -5
- package/dist/types/core/vector-index.d.ts +16 -0
- package/dist/types/index.d.ts +2 -1
- package/package.json +30 -7
- package/src/cli.ts +19 -19
- package/src/config.ts +98 -68
- package/src/core/banks.ts +2 -2
- package/src/core/beam/consolidate.ts +34 -5
- package/src/core/beam/helpers.ts +21 -28
- package/src/core/beam/index.ts +2 -2
- package/src/core/beam/recall.ts +98 -25
- package/src/core/beam/store.ts +96 -4
- package/src/core/binary-vectors.ts +1 -1
- package/src/core/content-sanitizer.ts +3 -3
- package/src/core/cost-log.ts +1 -1
- package/src/core/embeddings.ts +75 -50
- package/src/core/extraction/client.ts +44 -20
- package/src/core/extraction.ts +10 -9
- package/src/core/fastembed-runtime.ts +89 -0
- package/src/core/index.ts +1 -0
- package/src/core/llm-backends.ts +3 -0
- package/src/core/local-llm.ts +81 -43
- package/src/core/memory.ts +25 -5
- package/src/core/plugins.ts +1 -1
- package/src/core/polyphonic-recall.ts +4 -4
- package/src/core/query-cache.ts +2 -3
- package/src/core/runtime-options.ts +13 -5
- package/src/core/shmr.ts +141 -39
- package/src/core/streaming.ts +1 -1
- package/src/core/triples.ts +3 -3
- package/src/core/vector-index.ts +84 -0
- package/src/diagnose.ts +2 -2
- package/src/dr/recovery.ts +5 -5
- package/src/index.ts +1 -1
- package/src/mcp-server.ts +2 -2
- package/src/mcp-tools.ts +61 -61
package/src/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { hostMatchesUrl } from "@prometheus-ai/catalog/hosts";
|
|
3
4
|
import {
|
|
4
5
|
type Env,
|
|
5
6
|
envBool,
|
|
@@ -15,13 +16,13 @@ import {
|
|
|
15
16
|
export type { Env };
|
|
16
17
|
export { envBool, envDisabled, envFloat, envInt, envOneOf, envOptionalString, envString, envTruthy };
|
|
17
18
|
|
|
18
|
-
export const DEFAULT_DATA_DIR = join(homedir(), ".
|
|
19
|
-
export const DEFAULT_DB_FILENAME = "
|
|
20
|
-
export const FASTEMBED_CACHE_DIR = join(homedir(), ".
|
|
21
|
-
export const MODEL_CACHE_DIR = join(homedir(), ".
|
|
19
|
+
export const DEFAULT_DATA_DIR = join(homedir(), ".hermes", "mnemopi", "data");
|
|
20
|
+
export const DEFAULT_DB_FILENAME = "mnemopi.db";
|
|
21
|
+
export const FASTEMBED_CACHE_DIR = join(homedir(), ".hermes", "cache", "fastembed");
|
|
22
|
+
export const MODEL_CACHE_DIR = join(homedir(), ".hermes", "mnemopi", "models");
|
|
22
23
|
|
|
23
24
|
export const DEFAULT_EMBEDDING_MODEL = "BAAI/bge-small-en-v1.5";
|
|
24
|
-
export const
|
|
25
|
+
export const DEFAULT_EMBEDDING_APROMETHEUS_URL = "https://openrouter.ai/api/v1";
|
|
25
26
|
export const DEFAULT_LLM_MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF";
|
|
26
27
|
export const DEFAULT_LLM_MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf";
|
|
27
28
|
export const HOST_LLM_TIMEOUT_SECONDS = 15.0;
|
|
@@ -57,7 +58,7 @@ export const VERACITY_WEIGHT_DEFAULTS = {
|
|
|
57
58
|
} as const;
|
|
58
59
|
|
|
59
60
|
export function dataDir(env: Env = process.env): string {
|
|
60
|
-
return envOptionalString("
|
|
61
|
+
return envOptionalString("MNEMOPROMETHEUS_DATA_DIR", env) ?? DEFAULT_DATA_DIR;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export function dbPath(env: Env = process.env): string {
|
|
@@ -65,141 +66,149 @@ export function dbPath(env: Env = process.env): string {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export function beamOptimizationsEnabled(env: Env = process.env): boolean {
|
|
68
|
-
return envTruthy("
|
|
69
|
+
return envTruthy("MNEMOPROMETHEUS_BEAM_OPTIMIZATIONS", env);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export function embeddingModel(env: Env = process.env): string {
|
|
72
|
-
return envString("
|
|
73
|
+
return envString("MNEMOPROMETHEUS_EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL, env);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export function embeddingDim(env: Env = process.env): number {
|
|
76
|
-
const explicit = envInt("
|
|
77
|
+
const explicit = envInt("MNEMOPROMETHEUS_EMBEDDING_DIM", NaN, env);
|
|
77
78
|
if (Number.isFinite(explicit)) return explicit;
|
|
78
79
|
return EMBEDDING_DIMS[embeddingModel(env)] ?? 384;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
export function embeddingApiKey(env: Env = process.env): string {
|
|
82
83
|
return envString(
|
|
83
|
-
"
|
|
84
|
-
envString("
|
|
84
|
+
"MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_KEY",
|
|
85
|
+
envString("OPENROUTER_APROMETHEUS_KEY", envString("OPENAI_APROMETHEUS_KEY", "", env), env),
|
|
85
86
|
env,
|
|
86
87
|
);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
export function embeddingApiUrl(env: Env = process.env): string {
|
|
90
91
|
return envString(
|
|
91
|
-
"
|
|
92
|
-
envString("OPENROUTER_BASE_URL",
|
|
92
|
+
"MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
|
|
93
|
+
envString("OPENROUTER_BASE_URL", DEFAULT_EMBEDDING_APROMETHEUS_URL, env),
|
|
93
94
|
env,
|
|
94
95
|
);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
export function embeddingsViaApi(env: Env = process.env): boolean {
|
|
98
|
-
return envTruthy("
|
|
99
|
+
return envTruthy("MNEMOPROMETHEUS_EMBEDDINGS_VIA_API", env);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
export function embeddingsDisabled(env: Env = process.env): boolean {
|
|
102
|
-
return envString("
|
|
103
|
+
return envString("MNEMOPROMETHEUS_NO_EMBEDDINGS", "", env) !== "";
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
export function isApiEmbeddingModel(model = embeddingModel(), env: Env = process.env): boolean {
|
|
106
107
|
if (model.startsWith("openai/") || model.includes("text-embedding") || model.startsWith("text-embedding"))
|
|
107
108
|
return true;
|
|
108
|
-
const baseUrl = envString(
|
|
109
|
-
|
|
109
|
+
const baseUrl = envString(
|
|
110
|
+
"MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
|
|
111
|
+
envString("OPENROUTER_BASE_URL", "", env),
|
|
112
|
+
env,
|
|
113
|
+
);
|
|
114
|
+
if (baseUrl && !hostMatchesUrl(baseUrl, "openrouter")) return true;
|
|
110
115
|
return embeddingsViaApi(env);
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
export function apiEmbeddingsAvailable(env: Env = process.env): boolean {
|
|
114
119
|
if (embeddingsDisabled(env)) return false;
|
|
115
120
|
if (!isApiEmbeddingModel(embeddingModel(env), env)) return false;
|
|
116
|
-
const baseUrl = envString(
|
|
117
|
-
|
|
121
|
+
const baseUrl = envString(
|
|
122
|
+
"MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
|
|
123
|
+
envString("OPENROUTER_BASE_URL", "", env),
|
|
124
|
+
env,
|
|
125
|
+
);
|
|
126
|
+
return Boolean(baseUrl && !hostMatchesUrl(baseUrl, "openrouter")) || Boolean(embeddingApiKey(env));
|
|
118
127
|
}
|
|
119
128
|
|
|
120
129
|
export function workingMemoryMaxItems(env: Env = process.env): number {
|
|
121
|
-
return envInt("
|
|
130
|
+
return envInt("MNEMOPROMETHEUS_WM_MAX_ITEMS", 10000, env);
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
export function workingMemoryTtlHours(env: Env = process.env): number {
|
|
125
|
-
return envInt("
|
|
134
|
+
return envInt("MNEMOPROMETHEUS_WM_TTL_HOURS", 24, env);
|
|
126
135
|
}
|
|
127
136
|
|
|
128
137
|
export function episodicRecallLimit(env: Env = process.env): number {
|
|
129
|
-
return envInt("
|
|
138
|
+
return envInt("MNEMOPROMETHEUS_EP_LIMIT", 50000, env);
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
export function sleepBatchSize(env: Env = process.env): number {
|
|
133
|
-
return envInt("
|
|
142
|
+
return envInt("MNEMOPROMETHEUS_SLEEP_BATCH", 5000, env);
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
export function scratchpadMaxItems(env: Env = process.env): number {
|
|
137
|
-
return envInt("
|
|
146
|
+
return envInt("MNEMOPROMETHEUS_SP_MAX", 1000, env);
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
export function recencyHalflifeHours(env: Env = process.env): number {
|
|
141
|
-
return envFloat("
|
|
150
|
+
return envFloat("MNEMOPROMETHEUS_RECENCY_HALFLIFE", 168, env);
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
export function tier2Days(env: Env = process.env): number {
|
|
145
|
-
return envInt("
|
|
154
|
+
return envInt("MNEMOPROMETHEUS_TIER2_DAYS", 30, env);
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
export function tier3Days(env: Env = process.env): number {
|
|
149
|
-
return envInt("
|
|
158
|
+
return envInt("MNEMOPROMETHEUS_TIER3_DAYS", 180, env);
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
export function tier1Weight(env: Env = process.env): number {
|
|
153
|
-
return envFloat("
|
|
162
|
+
return envFloat("MNEMOPROMETHEUS_TIER1_WEIGHT", 1.0, env);
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
export function tier2Weight(env: Env = process.env): number {
|
|
157
|
-
return envFloat("
|
|
166
|
+
return envFloat("MNEMOPROMETHEUS_TIER2_WEIGHT", 0.5, env);
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
export function tier3Weight(env: Env = process.env): number {
|
|
161
|
-
return envFloat("
|
|
170
|
+
return envFloat("MNEMOPROMETHEUS_TIER3_WEIGHT", 0.25, env);
|
|
162
171
|
}
|
|
163
172
|
|
|
164
173
|
export function degradeBatchSize(env: Env = process.env): number {
|
|
165
|
-
return envInt("
|
|
174
|
+
return envInt("MNEMOPROMETHEUS_DEGRADE_BATCH", 100, env);
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
export function smartCompressEnabled(env: Env = process.env): boolean {
|
|
169
|
-
return !envDisabled("
|
|
178
|
+
return !envDisabled("MNEMOPROMETHEUS_SMART_COMPRESS", env);
|
|
170
179
|
}
|
|
171
180
|
|
|
172
181
|
export function tier3MaxChars(env: Env = process.env): number {
|
|
173
|
-
return envInt("
|
|
182
|
+
return envInt("MNEMOPROMETHEUS_TIER3_MAX_CHARS", 300, env);
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
export function statedWeight(env: Env = process.env): number {
|
|
177
|
-
return envFloat("
|
|
186
|
+
return envFloat("MNEMOPROMETHEUS_STATED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.stated, env);
|
|
178
187
|
}
|
|
179
188
|
|
|
180
189
|
export function inferredWeight(env: Env = process.env): number {
|
|
181
|
-
return envFloat("
|
|
190
|
+
return envFloat("MNEMOPROMETHEUS_INFERRED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.inferred, env);
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
export function toolWeight(env: Env = process.env): number {
|
|
185
|
-
return envFloat("
|
|
194
|
+
return envFloat("MNEMOPROMETHEUS_TOOL_WEIGHT", VERACITY_WEIGHT_DEFAULTS.tool, env);
|
|
186
195
|
}
|
|
187
196
|
|
|
188
197
|
export function importedWeight(env: Env = process.env): number {
|
|
189
|
-
return envFloat("
|
|
198
|
+
return envFloat("MNEMOPROMETHEUS_IMPORTED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.imported, env);
|
|
190
199
|
}
|
|
191
200
|
|
|
192
201
|
export function unknownWeight(env: Env = process.env): number {
|
|
193
|
-
return envFloat("
|
|
202
|
+
return envFloat("MNEMOPROMETHEUS_UNKNOWN_WEIGHT", VERACITY_WEIGHT_DEFAULTS.unknown, env);
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
export function veracityWeightOverrides(env: Env = process.env): string[] {
|
|
197
206
|
const names = [
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
207
|
+
"MNEMOPROMETHEUS_STATED_WEIGHT",
|
|
208
|
+
"MNEMOPROMETHEUS_INFERRED_WEIGHT",
|
|
209
|
+
"MNEMOPROMETHEUS_TOOL_WEIGHT",
|
|
210
|
+
"MNEMOPROMETHEUS_IMPORTED_WEIGHT",
|
|
211
|
+
"MNEMOPROMETHEUS_UNKNOWN_WEIGHT",
|
|
203
212
|
];
|
|
204
213
|
const overrides: string[] = [];
|
|
205
214
|
for (const name of names) {
|
|
@@ -209,19 +218,19 @@ export function veracityWeightOverrides(env: Env = process.env): string[] {
|
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
export function vecType(env: Env = process.env): VecType {
|
|
212
|
-
return envOneOf("
|
|
221
|
+
return envOneOf("MNEMOPROMETHEUS_VEC_TYPE", ["float32", "int8", "bit"] as const, "int8", env);
|
|
213
222
|
}
|
|
214
223
|
|
|
215
224
|
export function vectorWeight(env: Env = process.env): number {
|
|
216
|
-
return envFloat("
|
|
225
|
+
return envFloat("MNEMOPROMETHEUS_VEC_WEIGHT", 0.5, env);
|
|
217
226
|
}
|
|
218
227
|
|
|
219
228
|
export function ftsWeight(env: Env = process.env): number {
|
|
220
|
-
return envFloat("
|
|
229
|
+
return envFloat("MNEMOPROMETHEUS_FTS_WEIGHT", 0.3, env);
|
|
221
230
|
}
|
|
222
231
|
|
|
223
232
|
export function importanceWeight(env: Env = process.env): number {
|
|
224
|
-
return envFloat("
|
|
233
|
+
return envFloat("MNEMOPROMETHEUS_IMPORTANCE_WEIGHT", 0.2, env);
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
export function normalizedRecallWeights(
|
|
@@ -244,83 +253,104 @@ export function normalizedRecallWeights(
|
|
|
244
253
|
}
|
|
245
254
|
|
|
246
255
|
export function autoMigrateEnabled(env: Env = process.env): boolean {
|
|
247
|
-
return envString("
|
|
256
|
+
return envString("MNEMOPROMETHEUS_AUTO_MIGRATE", "1", env) !== "0";
|
|
248
257
|
}
|
|
249
258
|
|
|
250
259
|
export function proactiveLinkingEnabled(env: Env = process.env): boolean {
|
|
251
|
-
return envString("
|
|
260
|
+
return envString("MNEMOPROMETHEUS_PROACTIVE_LINKING", "0", env) === "1";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface RecallFeatureFlags {
|
|
264
|
+
polyphonicRecall?: boolean;
|
|
265
|
+
enhancedRecall?: boolean;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let polyphonicRecallDefault = false;
|
|
269
|
+
let enhancedRecallDefault = false;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Sets process-wide defaults for the env-gated recall features. Host configuration
|
|
273
|
+
* (e.g. the coding-agent `mnemopi.polyphonicRecall` / `mnemopi.enhancedRecall`
|
|
274
|
+
* settings) lands here; the `MNEMOPROMETHEUS_POLYPHONIC_RECALL` / `MNEMOPROMETHEUS_ENHANCED_RECALL`
|
|
275
|
+
* environment variables still win whenever they are set.
|
|
276
|
+
*/
|
|
277
|
+
export function configureRecallFeatures(flags: RecallFeatureFlags): void {
|
|
278
|
+
if (flags.polyphonicRecall !== undefined) polyphonicRecallDefault = flags.polyphonicRecall;
|
|
279
|
+
if (flags.enhancedRecall !== undefined) enhancedRecallDefault = flags.enhancedRecall;
|
|
252
280
|
}
|
|
253
281
|
|
|
254
282
|
export function polyphonicRecallEnabled(env: Env = process.env): boolean {
|
|
255
|
-
|
|
283
|
+
const value = envOptionalString("MNEMOPROMETHEUS_POLYPHONIC_RECALL", env);
|
|
284
|
+
return value === undefined ? polyphonicRecallDefault : value === "1";
|
|
256
285
|
}
|
|
257
286
|
|
|
258
287
|
export function temporalHalflifeHours(env: Env = process.env): number {
|
|
259
|
-
return envFloat("
|
|
288
|
+
return envFloat("MNEMOPROMETHEUS_TEMPORAL_HALFLIFE_HOURS", 24, env);
|
|
260
289
|
}
|
|
261
290
|
|
|
262
291
|
export function enhancedRecallEnabled(env: Env = process.env): boolean {
|
|
263
|
-
|
|
292
|
+
const value = envOptionalString("MNEMOPROMETHEUS_ENHANCED_RECALL", env);
|
|
293
|
+
return value === undefined ? enhancedRecallDefault : value === "1";
|
|
264
294
|
}
|
|
265
295
|
|
|
266
296
|
export function llmEnabled(env: Env = process.env): boolean {
|
|
267
|
-
return envBool("
|
|
297
|
+
return envBool("MNEMOPROMETHEUS_LLM_ENABLED", true, env);
|
|
268
298
|
}
|
|
269
299
|
|
|
270
300
|
export function llmMaxTokens(env: Env = process.env): number {
|
|
271
|
-
return envInt("
|
|
301
|
+
return envInt("MNEMOPROMETHEUS_LLM_MAX_TOKENS", 2048, env);
|
|
272
302
|
}
|
|
273
303
|
|
|
274
304
|
export function llmThreads(env: Env = process.env): number {
|
|
275
|
-
return envInt("
|
|
305
|
+
return envInt("MNEMOPROMETHEUS_LLM_N_THREADS", 4, env);
|
|
276
306
|
}
|
|
277
307
|
|
|
278
308
|
export function llmContext(env: Env = process.env): number {
|
|
279
|
-
return envInt("
|
|
309
|
+
return envInt("MNEMOPROMETHEUS_LLM_N_CTX", 2048, env);
|
|
280
310
|
}
|
|
281
311
|
|
|
282
312
|
export function llmRepo(env: Env = process.env): string {
|
|
283
|
-
return envString("
|
|
313
|
+
return envString("MNEMOPROMETHEUS_LLM_REPO", DEFAULT_LLM_MODEL_REPO, env);
|
|
284
314
|
}
|
|
285
315
|
|
|
286
316
|
export function llmFile(env: Env = process.env): string {
|
|
287
|
-
return envString("
|
|
317
|
+
return envString("MNEMOPROMETHEUS_LLM_FILE", DEFAULT_LLM_MODEL_FILE, env);
|
|
288
318
|
}
|
|
289
319
|
|
|
290
320
|
export function llmModelFiles(env: Env = process.env): readonly [repo: string, file: string] {
|
|
291
|
-
const repo = envOptionalString("
|
|
292
|
-
const file = envOptionalString("
|
|
321
|
+
const repo = envOptionalString("MNEMOPROMETHEUS_LLM_REPO", env);
|
|
322
|
+
const file = envOptionalString("MNEMOPROMETHEUS_LLM_FILE", env);
|
|
293
323
|
return repo && file ? [repo, file] : [DEFAULT_LLM_MODEL_REPO, DEFAULT_LLM_MODEL_FILE];
|
|
294
324
|
}
|
|
295
325
|
|
|
296
326
|
export function llmBaseUrl(env: Env = process.env): string {
|
|
297
|
-
return envString("
|
|
327
|
+
return envString("MNEMOPROMETHEUS_LLM_BASE_URL", "", env).replace(/\/+$/, "");
|
|
298
328
|
}
|
|
299
329
|
|
|
300
330
|
export function llmApiKey(env: Env = process.env): string {
|
|
301
|
-
return envString("
|
|
331
|
+
return envString("MNEMOPROMETHEUS_LLM_APROMETHEUS_KEY", "", env);
|
|
302
332
|
}
|
|
303
333
|
|
|
304
334
|
export function llmModel(env: Env = process.env): string {
|
|
305
|
-
return envString("
|
|
335
|
+
return envString("MNEMOPROMETHEUS_LLM_MODEL", "", env);
|
|
306
336
|
}
|
|
307
337
|
|
|
308
338
|
export function hostLlmEnabled(env: Env = process.env): boolean {
|
|
309
|
-
return envBool("
|
|
339
|
+
return envBool("MNEMOPROMETHEUS_HOST_LLM_ENABLED", false, env);
|
|
310
340
|
}
|
|
311
341
|
|
|
312
342
|
export function hostLlmProvider(env: Env = process.env): string | undefined {
|
|
313
|
-
return envOptionalString("
|
|
343
|
+
return envOptionalString("MNEMOPROMETHEUS_HOST_LLM_PROVIDER", env);
|
|
314
344
|
}
|
|
315
345
|
|
|
316
346
|
export function hostLlmModel(env: Env = process.env): string | undefined {
|
|
317
|
-
return envOptionalString("
|
|
347
|
+
return envOptionalString("MNEMOPROMETHEUS_HOST_LLM_MODEL", env);
|
|
318
348
|
}
|
|
319
349
|
|
|
320
350
|
export function hostLlmContext(env: Env = process.env): number {
|
|
321
|
-
return envInt("
|
|
351
|
+
return envInt("MNEMOPROMETHEUS_HOST_LLM_N_CTX", 32000, env);
|
|
322
352
|
}
|
|
323
353
|
|
|
324
354
|
export function sleepPrompt(env: Env = process.env): string {
|
|
325
|
-
return envString("
|
|
355
|
+
return envString("MNEMOPROMETHEUS_SLEEP_PROMPT", "", env).trim();
|
|
326
356
|
}
|
package/src/core/banks.ts
CHANGED
|
@@ -4,9 +4,9 @@ import { join } from "node:path";
|
|
|
4
4
|
import { dataDir as configuredDataDir } from "../config";
|
|
5
5
|
import { closeQuietly, openDatabase } from "../db";
|
|
6
6
|
|
|
7
|
-
export const DEFAULT_DATA_DIR = join(homedir(), ".
|
|
7
|
+
export const DEFAULT_DATA_DIR = join(homedir(), ".hermes", "mnemopi", "data");
|
|
8
8
|
export const BANKS_DIR = join(DEFAULT_DATA_DIR, "banks");
|
|
9
|
-
const DB_FILENAME = "
|
|
9
|
+
const DB_FILENAME = "mnemopi.db";
|
|
10
10
|
|
|
11
11
|
export class ValueError extends Error {
|
|
12
12
|
override name = "ValueError";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SQLQueryBindings } from "bun:sqlite";
|
|
2
2
|
import { generateId, stableMemoryId } from "../../util/ids";
|
|
3
3
|
import { aaakEncode } from "../aaak";
|
|
4
|
+
import { EpisodicGraph } from "../episodic-graph";
|
|
4
5
|
import { heuristicExtractFacts } from "../extraction";
|
|
5
6
|
import { clampVeracity } from "../veracity-consolidation";
|
|
6
7
|
import { scheduleEmbedding } from "./helpers";
|
|
@@ -51,11 +52,11 @@ function envInt(name: string, defaultValue: number): number {
|
|
|
51
52
|
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
const SLEEP_BATCH_SIZE = envInt("
|
|
55
|
-
const TIER2_DAYS = envInt("
|
|
56
|
-
const TIER3_DAYS = envInt("
|
|
57
|
-
const DEGRADE_BATCH_SIZE = envInt("
|
|
58
|
-
const TIER3_MAX_CHARS = envInt("
|
|
55
|
+
const SLEEP_BATCH_SIZE = envInt("MNEMOPROMETHEUS_SLEEP_BATCH", 5000);
|
|
56
|
+
const TIER2_DAYS = envInt("MNEMOPROMETHEUS_TIER2_DAYS", 30);
|
|
57
|
+
const TIER3_DAYS = envInt("MNEMOPROMETHEUS_TIER3_DAYS", 180);
|
|
58
|
+
const DEGRADE_BATCH_SIZE = envInt("MNEMOPROMETHEUS_DEGRADE_BATCH", 100);
|
|
59
|
+
const TIER3_MAX_CHARS = envInt("MNEMOPROMETHEUS_TIER3_MAX_CHARS", 300);
|
|
59
60
|
|
|
60
61
|
function isoNow(): string {
|
|
61
62
|
return new Date().toISOString();
|
|
@@ -261,6 +262,33 @@ function insertKg(
|
|
|
261
262
|
});
|
|
262
263
|
}
|
|
263
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Populate the episodic graph (gists, facts, edges) for a freshly consolidated
|
|
267
|
+
* memory. The graph backs Polyphonic Recall's `graph` voice, which relies on
|
|
268
|
+
* `findGistsByParticipant`, `findFactsBySubject`, and `findRelatedMemories`.
|
|
269
|
+
*
|
|
270
|
+
* Best-effort: failures (closed DB, missing optional tables) MUST NOT roll back
|
|
271
|
+
* the consolidation that already wrote the episodic row, so any throw here is
|
|
272
|
+
* swallowed. Unlike the proactive-link path in `store.ts` this is unconditional
|
|
273
|
+
* — consolidation is the explicit "settle and compress" step where the graph
|
|
274
|
+
* is supposed to gain edges.
|
|
275
|
+
*/
|
|
276
|
+
function ingestIntoEpisodicGraph(beam: BeamMemoryState, memoryId: string, summary: string): void {
|
|
277
|
+
try {
|
|
278
|
+
const graph =
|
|
279
|
+
beam.episodicGraph instanceof EpisodicGraph
|
|
280
|
+
? beam.episodicGraph
|
|
281
|
+
: new EpisodicGraph({ db: beam.db, dbPath: beam.dbPath });
|
|
282
|
+
graph.ingestMemory(summary, memoryId, {
|
|
283
|
+
sessionId: sourceSession(beam),
|
|
284
|
+
linkExisting: true,
|
|
285
|
+
extractEntities: true,
|
|
286
|
+
});
|
|
287
|
+
} catch {
|
|
288
|
+
// Graph enrichment is best-effort and never blocks consolidation.
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
264
292
|
export function consolidateToEpisodic(
|
|
265
293
|
beam: BeamMemoryState,
|
|
266
294
|
summary: string,
|
|
@@ -299,6 +327,7 @@ export function consolidateToEpisodic(
|
|
|
299
327
|
],
|
|
300
328
|
);
|
|
301
329
|
extractAndStoreFacts(beam, summary, 0, memoryId);
|
|
330
|
+
ingestIntoEpisodicGraph(beam, memoryId, summary);
|
|
302
331
|
scheduleEmbedding(beam, [{ memoryId, content: summary }]);
|
|
303
332
|
emitEvent(beam, "MEMORY_CONSOLIDATED", memoryId, summary, source, importance, {
|
|
304
333
|
summary_of: [...sourceWmIds],
|
package/src/core/beam/helpers.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { logger } from "@prometheus-ai/utils";
|
|
2
3
|
import { generateId as generateTimedId, sha256Hex16, stableMemoryId } from "../../util/ids";
|
|
3
4
|
import { currentEmbeddingModel, embed } from "../embeddings";
|
|
4
|
-
import { getMnemopiRuntimeOptions, withMnemopiRuntimeOptions } from "../runtime-options";
|
|
5
|
-
import {
|
|
5
|
+
import { getMnemopiRuntimeOptions, mnemopiDebugEnabled, withMnemopiRuntimeOptions } from "../runtime-options";
|
|
6
|
+
import { buildExactVectorIndex, searchExactVectorIndex } from "../vector-index";
|
|
6
7
|
import type { BeamMemoryState, JsonValue, Metadata } from "./types";
|
|
7
8
|
|
|
8
9
|
export type Vector = number[];
|
|
@@ -162,9 +163,9 @@ export function normalizeWeights(
|
|
|
162
163
|
ftsWeight: number | null | undefined,
|
|
163
164
|
importanceWeight: number | null | undefined,
|
|
164
165
|
): HybridWeights {
|
|
165
|
-
let vw = Math.max(0, vecWeight ?? envNumber("
|
|
166
|
-
let fw = Math.max(0, ftsWeight ?? envNumber("
|
|
167
|
-
let iw = Math.max(0, importanceWeight ?? envNumber("
|
|
166
|
+
let vw = Math.max(0, vecWeight ?? envNumber("MNEMOPROMETHEUS_VEC_WEIGHT", DEFAULT_WEIGHTS[0]));
|
|
167
|
+
let fw = Math.max(0, ftsWeight ?? envNumber("MNEMOPROMETHEUS_FTS_WEIGHT", DEFAULT_WEIGHTS[1]));
|
|
168
|
+
let iw = Math.max(0, importanceWeight ?? envNumber("MNEMOPROMETHEUS_IMPORTANCE_WEIGHT", DEFAULT_WEIGHTS[2]));
|
|
168
169
|
if (!Number.isFinite(vw)) vw = 0;
|
|
169
170
|
if (!Number.isFinite(fw)) fw = 0;
|
|
170
171
|
if (!Number.isFinite(iw)) iw = 0;
|
|
@@ -551,16 +552,10 @@ export function inMemoryVecSearch(db: Database, queryEmbedding: readonly number[
|
|
|
551
552
|
LIMIT 10000
|
|
552
553
|
`)
|
|
553
554
|
.all() as Record<string, unknown>[];
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const sim = vectorCosineSimilarity(queryEmbedding, vec);
|
|
559
|
-
if (sim === 0 && (queryEmbedding.every(n => n === 0) || vec.every(n => n === 0))) continue;
|
|
560
|
-
results.push({ rowid: Number(row.rowid), distance: 1 - sim });
|
|
561
|
-
}
|
|
562
|
-
results.sort((a, b) => a.distance - b.distance || a.rowid - b.rowid);
|
|
563
|
-
return results.slice(0, Math.max(0, Math.trunc(k)));
|
|
555
|
+
const index = buildExactVectorIndex(
|
|
556
|
+
rows.map(row => ({ id: Number(row.rowid), vector: decodeVector(String(row.embedding_json ?? "")) })),
|
|
557
|
+
);
|
|
558
|
+
return searchExactVectorIndex(index, queryEmbedding, k).map(hit => ({ rowid: hit.id, distance: 1 - hit.score }));
|
|
564
559
|
} catch {
|
|
565
560
|
return [];
|
|
566
561
|
}
|
|
@@ -574,7 +569,7 @@ export function workingMemoryVecSearch(
|
|
|
574
569
|
): WorkingVectorResult[] {
|
|
575
570
|
if (queryEmbedding.length === 0) return [];
|
|
576
571
|
try {
|
|
577
|
-
const limit = process.env.
|
|
572
|
+
const limit = process.env.MNEMOPROMETHEUS_BEAM_MODE ? 500_000 : 50_000;
|
|
578
573
|
const rows = db
|
|
579
574
|
.query(`
|
|
580
575
|
SELECT wm.id, me.embedding_json
|
|
@@ -585,16 +580,10 @@ export function workingMemoryVecSearch(
|
|
|
585
580
|
LIMIT ?
|
|
586
581
|
`)
|
|
587
582
|
.all(now.toISOString(), limit) as Record<string, unknown>[];
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const sim = vectorCosineSimilarity(queryEmbedding, vec);
|
|
593
|
-
if (sim === 0 && (queryEmbedding.every(n => n === 0) || vec.every(n => n === 0))) continue;
|
|
594
|
-
results.push({ id: String(row.id), sim });
|
|
595
|
-
}
|
|
596
|
-
results.sort((a, b) => b.sim - a.sim || a.id.localeCompare(b.id));
|
|
597
|
-
return results.slice(0, Math.max(0, Math.trunc(k)));
|
|
583
|
+
const index = buildExactVectorIndex(
|
|
584
|
+
rows.map(row => ({ id: String(row.id), vector: decodeVector(String(row.embedding_json ?? "")) })),
|
|
585
|
+
);
|
|
586
|
+
return searchExactVectorIndex(index, queryEmbedding, k).map(hit => ({ id: hit.id, sim: hit.score }));
|
|
598
587
|
} catch {
|
|
599
588
|
return [];
|
|
600
589
|
}
|
|
@@ -944,12 +933,16 @@ async function runEmbedding(beam: BeamMemoryState, items: readonly EmbedItem[]):
|
|
|
944
933
|
}
|
|
945
934
|
});
|
|
946
935
|
insertMany(items);
|
|
947
|
-
} catch {
|
|
936
|
+
} catch (error) {
|
|
948
937
|
// Background embedding generation is best-effort: a failing provider, a closed DB
|
|
949
938
|
// during shutdown, or a transient API error must never disrupt the synchronous
|
|
950
939
|
// remember()/consolidate() that scheduled it. Production recall silently degrades
|
|
951
940
|
// to FTS-only for the affected rows, which is the same shape as a misconfigured
|
|
952
|
-
// provider.
|
|
941
|
+
// provider. Log so the failure is diagnosable (#2322).
|
|
942
|
+
logger[mnemopiDebugEnabled() ? "warn" : "debug"]("mnemopi: background embedding failed", {
|
|
943
|
+
itemCount: items.length,
|
|
944
|
+
error: String(error),
|
|
945
|
+
});
|
|
953
946
|
}
|
|
954
947
|
}
|
|
955
948
|
|
package/src/core/beam/index.ts
CHANGED
|
@@ -87,14 +87,14 @@ function normalizeConfig(options: BeamMemoryOptions): BeamConfig {
|
|
|
87
87
|
function autoMigrateAnnotations(db: Database, dbPath: string | undefined): void {
|
|
88
88
|
if (dbPath === undefined || dbPath === ":memory:" || !existsSync(dbPath)) return;
|
|
89
89
|
if (!hasPendingMigration(db)) return;
|
|
90
|
-
if (process.env.
|
|
90
|
+
if (process.env.MNEMOPROMETHEUS_AUTO_MIGRATE === "0") {
|
|
91
91
|
const row = db
|
|
92
92
|
.query(
|
|
93
93
|
"SELECT COUNT(*) AS count FROM triples WHERE predicate IN ('mentions', 'fact', 'occurred_on', 'has_source')",
|
|
94
94
|
)
|
|
95
95
|
.get() as { count: number };
|
|
96
96
|
console.warn(
|
|
97
|
-
`
|
|
97
|
+
`MNEMOPROMETHEUS_AUTO_MIGRATE=0: ${row.count} annotation rows pending; run scripts/migrate_triplestore_split.py manually.`,
|
|
98
98
|
);
|
|
99
99
|
return;
|
|
100
100
|
}
|