@prometheus-ai/memory 0.5.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/README.md +107 -0
- package/dist/types/cli.d.ts +35 -0
- package/dist/types/config.d.ts +77 -0
- package/dist/types/core/aaak.d.ts +55 -0
- package/dist/types/core/annotations.d.ts +75 -0
- package/dist/types/core/banks.d.ts +33 -0
- package/dist/types/core/beam/consolidate.d.ts +32 -0
- package/dist/types/core/beam/helpers.d.ts +76 -0
- package/dist/types/core/beam/index.d.ts +59 -0
- package/dist/types/core/beam/recall.d.ts +32 -0
- package/dist/types/core/beam/schema.d.ts +2 -0
- package/dist/types/core/beam/store.d.ts +35 -0
- package/dist/types/core/beam/types.d.ts +233 -0
- package/dist/types/core/binary-vectors.d.ts +54 -0
- package/dist/types/core/chat-normalize.d.ts +13 -0
- package/dist/types/core/content-sanitizer.d.ts +18 -0
- package/dist/types/core/cost-log.d.ts +13 -0
- package/dist/types/core/embeddings.d.ts +44 -0
- package/dist/types/core/entities.d.ts +7 -0
- package/dist/types/core/episodic-graph.d.ts +89 -0
- package/dist/types/core/extraction/client.d.ts +31 -0
- package/dist/types/core/extraction/diagnostics.d.ts +51 -0
- package/dist/types/core/extraction/prompts.d.ts +2 -0
- package/dist/types/core/extraction.d.ts +6 -0
- package/dist/types/core/index.d.ts +4 -0
- package/dist/types/core/llm-backends.d.ts +21 -0
- package/dist/types/core/local-llm.d.ts +15 -0
- package/dist/types/core/memory.d.ts +160 -0
- package/dist/types/core/migrations/e6-triplestore-split.d.ts +17 -0
- package/dist/types/core/migrations/index.d.ts +1 -0
- package/dist/types/core/mmr.d.ts +8 -0
- package/dist/types/core/orchestrator.d.ts +20 -0
- package/dist/types/core/patterns.d.ts +61 -0
- package/dist/types/core/plugins.d.ts +109 -0
- package/dist/types/core/polyphonic-recall.d.ts +66 -0
- package/dist/types/core/query-cache.d.ts +46 -0
- package/dist/types/core/query-intent.d.ts +20 -0
- package/dist/types/core/recall-diagnostics.d.ts +48 -0
- package/dist/types/core/runtime-options.d.ts +68 -0
- package/dist/types/core/shmr.d.ts +56 -0
- package/dist/types/core/streaming.d.ts +136 -0
- package/dist/types/core/synonyms.d.ts +46 -0
- package/dist/types/core/temporal-parser.d.ts +16 -0
- package/dist/types/core/token-counter.d.ts +8 -0
- package/dist/types/core/triples.d.ts +63 -0
- package/dist/types/core/typed-memory.d.ts +39 -0
- package/dist/types/core/vector-math.d.ts +1 -0
- package/dist/types/core/veracity-consolidation.d.ts +60 -0
- package/dist/types/core/weibull.d.ts +96 -0
- package/dist/types/db.d.ts +16 -0
- package/dist/types/diagnose.d.ts +24 -0
- package/dist/types/dr/index.d.ts +1 -0
- package/dist/types/dr/recovery.d.ts +68 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/mcp-server.d.ts +40 -0
- package/dist/types/mcp-tools.d.ts +484 -0
- package/dist/types/migrations/e6-triplestore-split.d.ts +1 -0
- package/dist/types/migrations/index.d.ts +1 -0
- package/dist/types/types.d.ts +145 -0
- package/dist/types/util/datetime.d.ts +8 -0
- package/dist/types/util/env.d.ts +10 -0
- package/dist/types/util/ids.d.ts +3 -0
- package/dist/types/util/lru.d.ts +12 -0
- package/dist/types/util/regex.d.ts +10 -0
- package/package.json +85 -0
- package/src/cli.ts +398 -0
- package/src/config.ts +326 -0
- package/src/core/aaak.ts +142 -0
- package/src/core/annotations.ts +457 -0
- package/src/core/banks.ts +133 -0
- package/src/core/beam/consolidate.ts +965 -0
- package/src/core/beam/helpers.ts +977 -0
- package/src/core/beam/index.ts +353 -0
- package/src/core/beam/recall.ts +1100 -0
- package/src/core/beam/schema.ts +423 -0
- package/src/core/beam/store.ts +829 -0
- package/src/core/beam/types.ts +268 -0
- package/src/core/binary-vectors.ts +317 -0
- package/src/core/chat-normalize.ts +160 -0
- package/src/core/content-sanitizer.ts +136 -0
- package/src/core/cost-log.ts +103 -0
- package/src/core/embeddings.ts +423 -0
- package/src/core/entities.ts +259 -0
- package/src/core/episodic-graph.ts +708 -0
- package/src/core/extraction/client.ts +162 -0
- package/src/core/extraction/diagnostics.ts +193 -0
- package/src/core/extraction/prompts.ts +31 -0
- package/src/core/extraction.ts +335 -0
- package/src/core/index.ts +30 -0
- package/src/core/llm-backends.ts +51 -0
- package/src/core/local-llm.ts +436 -0
- package/src/core/memory.ts +630 -0
- package/src/core/migrations/e6-triplestore-split.ts +211 -0
- package/src/core/migrations/index.ts +1 -0
- package/src/core/mmr.ts +71 -0
- package/src/core/orchestrator.ts +62 -0
- package/src/core/patterns.ts +484 -0
- package/src/core/plugins.ts +375 -0
- package/src/core/polyphonic-recall.ts +563 -0
- package/src/core/query-cache.ts +354 -0
- package/src/core/query-intent.ts +139 -0
- package/src/core/recall-diagnostics.ts +157 -0
- package/src/core/runtime-options.ts +119 -0
- package/src/core/shmr.ts +460 -0
- package/src/core/streaming.ts +419 -0
- package/src/core/synonyms.ts +197 -0
- package/src/core/temporal-parser.ts +363 -0
- package/src/core/token-counter.ts +30 -0
- package/src/core/triples.ts +454 -0
- package/src/core/typed-memory.ts +407 -0
- package/src/core/vector-math.ts +23 -0
- package/src/core/veracity-consolidation.ts +477 -0
- package/src/core/weibull.ts +124 -0
- package/src/db.ts +128 -0
- package/src/diagnose.ts +174 -0
- package/src/dr/index.ts +1 -0
- package/src/dr/recovery.ts +405 -0
- package/src/index.ts +33 -0
- package/src/mcp-server.ts +155 -0
- package/src/mcp-tools.ts +970 -0
- package/src/migrations/e6-triplestore-split.ts +1 -0
- package/src/migrations/index.ts +1 -0
- package/src/types.ts +157 -0
- package/src/util/datetime.ts +69 -0
- package/src/util/env.ts +65 -0
- package/src/util/ids.ts +19 -0
- package/src/util/lru.ts +48 -0
- package/src/util/regex.ts +165 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { getDiagnostics, safeForLog } from "./extraction/diagnostics";
|
|
2
|
+
import { callHostLlm, getHostLlmBackend } from "./llm-backends";
|
|
3
|
+
import {
|
|
4
|
+
callConfiguredCompletion,
|
|
5
|
+
callLocalLlm,
|
|
6
|
+
callRemoteLlm,
|
|
7
|
+
cleanOutput,
|
|
8
|
+
configuredLlmWillHandleCall,
|
|
9
|
+
llmAvailable,
|
|
10
|
+
} from "./local-llm";
|
|
11
|
+
import { getMnemopiRuntimeOptions } from "./runtime-options";
|
|
12
|
+
|
|
13
|
+
const TRUE_VALUES: Record<string, true> = { "1": true, true: true, yes: true, on: true };
|
|
14
|
+
|
|
15
|
+
function env(name: string): string {
|
|
16
|
+
return process.env[name] ?? "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function envBool(name: string, defaultValue: boolean): boolean {
|
|
20
|
+
const value = env(name).trim().toLowerCase();
|
|
21
|
+
return value === "" ? defaultValue : TRUE_VALUES[value] === true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function envInt(name: string, defaultValue: number): number {
|
|
25
|
+
const parsed = Number.parseInt(env(name), 10);
|
|
26
|
+
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function llmEnabled(): boolean {
|
|
30
|
+
return envBool("PROMETHEUS_MEMORY_LLM_ENABLED", true);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function hostLlmEnabled(): boolean {
|
|
34
|
+
return envBool("PROMETHEUS_MEMORY_HOST_LLM_ENABLED", false);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function llmBaseUrl(): string {
|
|
38
|
+
return env("PROMETHEUS_MEMORY_LLM_BASE_URL").replace(/\/+$/, "");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function llmMaxTokens(): number {
|
|
42
|
+
return envInt("PROMETHEUS_MEMORY_LLM_MAX_TOKENS", 2048);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const EXTRACTION_PROMPT_TEMPLATE =
|
|
46
|
+
env("PROMETHEUS_MEMORY_EXTRACTION_PROMPT") ||
|
|
47
|
+
`You are an expert structured memory extractor for Mnemopi v3.0+ MEMORIA tables.
|
|
48
|
+
The user message below may be in English, German, Russian, or another language.
|
|
49
|
+
First detect the language, then extract ONLY high-signal, long-term relevant items.
|
|
50
|
+
Categories to extract (return valid JSON only, no extra text):
|
|
51
|
+
- facts: persistent user metrics, states, knowledge, or personal data
|
|
52
|
+
(Examples: 'my name is X', 'I work at Y', 'server runs on port 8080')
|
|
53
|
+
- instructions: rules or commands directed at me the agent
|
|
54
|
+
(Examples: 'always use tabs', 'never delete logs', 'call me boss')
|
|
55
|
+
- preferences: likes, dislikes, and their evolution
|
|
56
|
+
(Examples: 'I like dark mode', 'I prefer Python over Go')
|
|
57
|
+
- timelines: real events with dates/times
|
|
58
|
+
(Examples: 'release on 2024-12-01', 'meeting next Tuesday')
|
|
59
|
+
- kg: knowledge-graph triples in subject-predicate-object form
|
|
60
|
+
|
|
61
|
+
Rules:
|
|
62
|
+
- Only extract persistent, non-transient content. Ignore weather, one-off chat, system text.
|
|
63
|
+
- Use semantic understanding — do NOT rely on English keywords.
|
|
64
|
+
- Preserve original casing and language.
|
|
65
|
+
- If nothing qualifies, return empty arrays.
|
|
66
|
+
|
|
67
|
+
Return JSON in this exact format:
|
|
68
|
+
{"facts": [], "instructions": [], "preferences": [], "timelines": [], "kg": []}
|
|
69
|
+
|
|
70
|
+
User message: {text}
|
|
71
|
+
|
|
72
|
+
Extraction:`;
|
|
73
|
+
|
|
74
|
+
export function buildExtractionPrompt(text: string, detectedLang = "en"): string {
|
|
75
|
+
const template = getMnemopiRuntimeOptions()?.llm?.extractionPrompt ?? EXTRACTION_PROMPT_TEMPLATE;
|
|
76
|
+
return template.split("{text}").join(text).split("{lang}").join(detectedLang);
|
|
77
|
+
}
|
|
78
|
+
function stripFence(raw: string): string {
|
|
79
|
+
let s = raw.trim();
|
|
80
|
+
if (!s.startsWith("```")) {
|
|
81
|
+
return s;
|
|
82
|
+
}
|
|
83
|
+
s = s.replace(/^```(?:json)?\s*/i, "");
|
|
84
|
+
s = s.replace(/\s*```$/i, "");
|
|
85
|
+
return s.trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function normalizeFact(fact: string): string {
|
|
89
|
+
const trimmed = fact.trim();
|
|
90
|
+
// Remove trailing sentence punctuation (. ! ?) if present
|
|
91
|
+
return trimmed.replace(/[.!?]+$/, "");
|
|
92
|
+
}
|
|
93
|
+
export function parseFacts(rawOutput: string | null | undefined): string[] {
|
|
94
|
+
if (rawOutput === null || rawOutput === undefined) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const raw = rawOutput.trim();
|
|
98
|
+
if (raw === "" || raw.toUpperCase() === "NO_FACTS") {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const rawClean = stripFence(raw);
|
|
102
|
+
if (rawClean.startsWith("{")) {
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(rawClean) as unknown;
|
|
105
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
106
|
+
const obj = parsed as Record<string, unknown>;
|
|
107
|
+
const out: string[] = [];
|
|
108
|
+
for (const category of ["facts", "instructions", "preferences", "timelines"] as const) {
|
|
109
|
+
const items = obj[category];
|
|
110
|
+
if (Array.isArray(items)) {
|
|
111
|
+
for (const item of items) {
|
|
112
|
+
if (item !== null && item !== undefined && String(item).trim() !== "") {
|
|
113
|
+
const normalized = normalizeFact(String(item));
|
|
114
|
+
if (normalized !== "") {
|
|
115
|
+
out.push(normalized);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (out.length > 0) {
|
|
122
|
+
return out.slice(0, 5);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
const matches = [...raw.matchAll(/"([^"]{10,})"/g)].map(m => m[1]).filter((v): v is string => v !== undefined);
|
|
127
|
+
if (matches.length > 0) {
|
|
128
|
+
return matches
|
|
129
|
+
.map(normalizeFact)
|
|
130
|
+
.filter(f => f !== "")
|
|
131
|
+
.slice(0, 5);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const cleaned: string[] = [];
|
|
136
|
+
for (const line of raw.split("\n")) {
|
|
137
|
+
const fact = line.replace(/^[\s\d.\-*]+/, "").trim();
|
|
138
|
+
if (fact.length > 10) {
|
|
139
|
+
const normalized = normalizeFact(fact);
|
|
140
|
+
if (normalized !== "") {
|
|
141
|
+
cleaned.push(normalized);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return cleaned.slice(0, 5);
|
|
146
|
+
}
|
|
147
|
+
function sentenceCase(value: string): string {
|
|
148
|
+
const trimmed = value.trim().replace(/[.!?]+$/, "");
|
|
149
|
+
return trimmed === "" ? "" : `${trimmed[0]?.toUpperCase() ?? ""}${trimmed.slice(1)}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function addUnique(out: string[], value: string): void {
|
|
153
|
+
const fact = sentenceCase(value);
|
|
154
|
+
if (fact.length > 10 && !out.includes(fact)) {
|
|
155
|
+
out.push(fact);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function heuristicExtractFacts(text: string): string[] {
|
|
160
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
161
|
+
if (normalized === "") {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const facts: string[] = [];
|
|
165
|
+
const clauses = normalized.split(/(?:[.!?;]+|\s+and\s+|\s+but\s+)/i);
|
|
166
|
+
for (const clause of clauses) {
|
|
167
|
+
const c = clause.trim();
|
|
168
|
+
let value = /\bmy name is\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
169
|
+
if (value !== undefined) addUnique(facts, `The user's name is ${value}`);
|
|
170
|
+
value = /\bi (?:am|work as)\s+(?:an?\s+)?([^,.!?;]+)/i.exec(c)?.[1];
|
|
171
|
+
if (value !== undefined) addUnique(facts, `The user is ${value}`);
|
|
172
|
+
value = /\bi work (?:at|for)\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
173
|
+
if (value !== undefined) addUnique(facts, `The user works at ${value}`);
|
|
174
|
+
value = /\bi (?:live in|am based in)\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
175
|
+
if (value !== undefined) addUnique(facts, `The user lives in ${value}`);
|
|
176
|
+
value = /\bi (?:use|uses|am using)\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
177
|
+
if (value !== undefined) addUnique(facts, `The user uses ${value}`);
|
|
178
|
+
value = /\bi (?:like|love|prefer|enjoy)\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
179
|
+
if (value !== undefined) addUnique(facts, `The user prefers ${value}`);
|
|
180
|
+
value = /\bi (?:hate|dislike|do not like|don't like)\s+([^,.!?;]+)/i.exec(c)?.[1];
|
|
181
|
+
if (value !== undefined) addUnique(facts, `The user dislikes ${value}`);
|
|
182
|
+
const instruction = /\b(always|never)\s+([^,.!?;]+)/i.exec(c);
|
|
183
|
+
if (instruction?.[1] !== undefined && instruction[2] !== undefined) {
|
|
184
|
+
addUnique(facts, `Instruction: ${instruction[1].toLowerCase()} ${instruction[2]}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return facts.slice(0, 5);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function tryHostExtraction(prompt: string): Promise<[boolean, string | null]> {
|
|
191
|
+
if (!llmEnabled() || !hostLlmEnabled() || getHostLlmBackend() === null) {
|
|
192
|
+
return [false, null];
|
|
193
|
+
}
|
|
194
|
+
const raw = await callHostLlm(prompt, {
|
|
195
|
+
maxTokens: llmMaxTokens(),
|
|
196
|
+
temperature: 0,
|
|
197
|
+
timeout: 15,
|
|
198
|
+
provider: env("PROMETHEUS_MEMORY_HOST_LLM_PROVIDER").trim() || null,
|
|
199
|
+
model: env("PROMETHEUS_MEMORY_HOST_LLM_MODEL").trim() || null,
|
|
200
|
+
});
|
|
201
|
+
const text = typeof raw === "string" ? raw.trim() : "";
|
|
202
|
+
return [true, text === "" ? null : text];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function localFallback(prompt: string, sourceText: string, diag = getDiagnostics()): Promise<string[]> {
|
|
206
|
+
diag.recordAttempt("local");
|
|
207
|
+
try {
|
|
208
|
+
const raw = await callLocalLlm(prompt);
|
|
209
|
+
if (raw !== null) {
|
|
210
|
+
const facts = parseFacts(cleanOutput(raw));
|
|
211
|
+
if (facts.length > 0) {
|
|
212
|
+
diag.recordSuccess("local", facts.length);
|
|
213
|
+
diag.recordCall({ succeeded: true });
|
|
214
|
+
return facts;
|
|
215
|
+
}
|
|
216
|
+
diag.recordNoOutput("local");
|
|
217
|
+
}
|
|
218
|
+
} catch (exc) {
|
|
219
|
+
diag.recordFailure("local", exc, "local_llm_raised");
|
|
220
|
+
diag.recordCall({ succeeded: false });
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
diag.recordFailure("local", undefined, "model_not_loaded");
|
|
224
|
+
const heuristic = heuristicExtractFacts(sourceText);
|
|
225
|
+
if (heuristic.length > 0) {
|
|
226
|
+
diag.recordSuccess("local", heuristic.length);
|
|
227
|
+
diag.recordCall({ succeeded: true });
|
|
228
|
+
return heuristic;
|
|
229
|
+
}
|
|
230
|
+
diag.recordCall({ succeeded: false, allEmpty: true });
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function extractFacts(text: string | null | undefined): Promise<string[]> {
|
|
235
|
+
const diag = getDiagnostics();
|
|
236
|
+
if (typeof text !== "string" || text.trim() === "") {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const prompt = buildExtractionPrompt(text);
|
|
240
|
+
|
|
241
|
+
// Configured completion (host-injected runtime LLM, e.g. the coding-agent's smol
|
|
242
|
+
// or a local on-device model). Mirrors consolidation's precedence: when a
|
|
243
|
+
// complete() fn is wired, it is the chosen path. Extraction is deterministic
|
|
244
|
+
// (temperature 0) so re-ingesting the same content does not create near-dupes.
|
|
245
|
+
if (configuredLlmWillHandleCall()) {
|
|
246
|
+
diag.recordAttempt("host");
|
|
247
|
+
try {
|
|
248
|
+
const raw = await callConfiguredCompletion(prompt, 0, { maxTokens: llmMaxTokens() });
|
|
249
|
+
if (typeof raw === "string" && raw.trim() !== "") {
|
|
250
|
+
const facts = parseFacts(raw);
|
|
251
|
+
if (facts.length > 0) {
|
|
252
|
+
diag.recordSuccess("host", facts.length);
|
|
253
|
+
diag.recordCall({ succeeded: true });
|
|
254
|
+
return facts;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
diag.recordNoOutput("host");
|
|
258
|
+
} catch (exc) {
|
|
259
|
+
diag.recordFailure("host", exc, "configured_completion_raised");
|
|
260
|
+
diag.recordCall({ succeeded: false });
|
|
261
|
+
console.warn(`extractFacts: configured completion raised: ${safeForLog(exc)}`);
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
return localFallback(prompt, text, diag);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const [attempted, hostText] = await tryHostExtraction(prompt);
|
|
269
|
+
if (attempted) {
|
|
270
|
+
diag.recordAttempt("host");
|
|
271
|
+
if (hostText !== null) {
|
|
272
|
+
const facts = parseFacts(hostText);
|
|
273
|
+
if (facts.length > 0) {
|
|
274
|
+
diag.recordSuccess("host", facts.length);
|
|
275
|
+
diag.recordCall({ succeeded: true });
|
|
276
|
+
return facts;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
diag.recordNoOutput("host");
|
|
280
|
+
return localFallback(prompt, text, diag);
|
|
281
|
+
}
|
|
282
|
+
} catch (exc) {
|
|
283
|
+
diag.recordAttempt("host");
|
|
284
|
+
diag.recordFailure("host", exc, "host_adapter_raised");
|
|
285
|
+
diag.recordCall({ succeeded: false });
|
|
286
|
+
console.warn(`extractFacts: host LLM adapter raised: ${safeForLog(exc)}`);
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!llmAvailable()) {
|
|
291
|
+
diag.recordAttempt("local");
|
|
292
|
+
const heuristic = heuristicExtractFacts(text);
|
|
293
|
+
if (heuristic.length > 0) {
|
|
294
|
+
diag.recordSuccess("local", heuristic.length);
|
|
295
|
+
diag.recordCall({ succeeded: true });
|
|
296
|
+
return heuristic;
|
|
297
|
+
}
|
|
298
|
+
diag.recordFailure("local", undefined, "llm_unavailable_at_call_site");
|
|
299
|
+
diag.recordCall({ succeeded: false });
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (llmEnabled() && llmBaseUrl() !== "") {
|
|
304
|
+
diag.recordAttempt("remote");
|
|
305
|
+
try {
|
|
306
|
+
const raw = await callRemoteLlm(prompt, 0);
|
|
307
|
+
if (raw !== null) {
|
|
308
|
+
const facts = parseFacts(cleanOutput(raw));
|
|
309
|
+
if (facts.length > 0) {
|
|
310
|
+
diag.recordSuccess("remote", facts.length);
|
|
311
|
+
diag.recordCall({ succeeded: true });
|
|
312
|
+
return facts;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
diag.recordNoOutput("remote");
|
|
316
|
+
} catch (exc) {
|
|
317
|
+
diag.recordFailure("remote", exc, "remote_call_raised");
|
|
318
|
+
console.warn(`extractFacts: remote LLM raised: ${safeForLog(exc)}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return localFallback(prompt, text, diag);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export async function extractFactsSafe(text: string | null | undefined): Promise<string[]> {
|
|
326
|
+
try {
|
|
327
|
+
return await extractFacts(text);
|
|
328
|
+
} catch (exc) {
|
|
329
|
+
const diag = getDiagnostics();
|
|
330
|
+
diag.recordFailure("wrapper", exc, "outer_wrapper_caught");
|
|
331
|
+
diag.recordCall({ succeeded: false });
|
|
332
|
+
console.warn(`extractFactsSafe: extractFacts() raised: ${safeForLog(exc)}`);
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from "./banks";
|
|
2
|
+
export * from "./beam/index";
|
|
3
|
+
export * from "./memory";
|
|
4
|
+
export {
|
|
5
|
+
addMemory,
|
|
6
|
+
forget,
|
|
7
|
+
get,
|
|
8
|
+
getBank,
|
|
9
|
+
getContext,
|
|
10
|
+
getDefaultInstance,
|
|
11
|
+
getStats,
|
|
12
|
+
Mnemopi,
|
|
13
|
+
query,
|
|
14
|
+
recall,
|
|
15
|
+
recallEnhanced,
|
|
16
|
+
remember,
|
|
17
|
+
resetDefaultInstanceForTests,
|
|
18
|
+
resetMemoryForTests,
|
|
19
|
+
resetModuleStateForTests,
|
|
20
|
+
saveMemory,
|
|
21
|
+
scratchpadClear,
|
|
22
|
+
scratchpadRead,
|
|
23
|
+
scratchpadWrite,
|
|
24
|
+
search,
|
|
25
|
+
setBank,
|
|
26
|
+
sleep,
|
|
27
|
+
sleepAllSessions,
|
|
28
|
+
storeMemory,
|
|
29
|
+
update,
|
|
30
|
+
} from "./memory";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface CompleteOptions {
|
|
2
|
+
maxTokens?: number;
|
|
3
|
+
temperature?: number;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
provider?: string | null;
|
|
6
|
+
model?: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LlmBackend {
|
|
10
|
+
name?: string;
|
|
11
|
+
complete(prompt: string, opts?: CompleteOptions): string | null | Promise<string | null>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let hostBackend: LlmBackend | null = null;
|
|
15
|
+
|
|
16
|
+
export function setHostLlmBackend(backend: LlmBackend | null | undefined): void {
|
|
17
|
+
hostBackend = backend ?? null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getHostLlmBackend(): LlmBackend | null {
|
|
21
|
+
return hostBackend;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resetHostLlmBackendForTests(): void {
|
|
25
|
+
hostBackend = null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function callHostLlm(prompt: string, opts: CompleteOptions = {}): Promise<string | null> {
|
|
29
|
+
const backend = getHostLlmBackend();
|
|
30
|
+
if (backend === null) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const result = await backend.complete(prompt, opts);
|
|
36
|
+
return typeof result === "string" ? result : null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class CallableLlmBackend implements LlmBackend {
|
|
43
|
+
constructor(
|
|
44
|
+
public name: string,
|
|
45
|
+
private readonly fn: (prompt: string, opts?: CompleteOptions) => string | null | Promise<string | null>,
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
complete(prompt: string, opts?: CompleteOptions): string | null | Promise<string | null> {
|
|
49
|
+
return this.fn(prompt, opts);
|
|
50
|
+
}
|
|
51
|
+
}
|