superlocalmemory 3.0.16 → 3.0.18
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/bin/slm-npm +8 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/superlocalmemory/cli/commands.py +29 -0
- package/src/superlocalmemory/cli/main.py +94 -30
- package/src/superlocalmemory/core/embedding_worker.py +120 -0
- package/src/superlocalmemory/core/embeddings.py +156 -240
- package/src/superlocalmemory/core/recall_worker.py +193 -0
- package/src/superlocalmemory/core/summarizer.py +182 -0
- package/src/superlocalmemory/core/worker_pool.py +209 -0
- package/src/superlocalmemory/mcp/server.py +9 -0
- package/src/superlocalmemory/mcp/tools_core.py +21 -8
- package/src/superlocalmemory/mcp/tools_v3.py +21 -0
- package/src/superlocalmemory/server/routes/helpers.py +21 -0
- package/src/superlocalmemory/server/routes/memories.py +100 -42
- package/src/superlocalmemory/server/routes/stats.py +11 -0
- package/src/superlocalmemory/server/routes/v3_api.py +195 -43
- package/src/superlocalmemory/server/ui.py +15 -14
- package/src/superlocalmemory/storage/database.py +23 -0
- package/src/superlocalmemory.egg-info/PKG-INFO +1 -1
- package/src/superlocalmemory.egg-info/SOURCES.txt +4 -0
- package/ui/index.html +113 -29
- package/ui/js/auto-settings.js +330 -1
- package/ui/js/clusters.js +138 -101
- package/ui/js/graph-core.js +3 -1
- package/ui/js/graph-interactions.js +2 -5
- package/ui/js/memories.js +65 -2
- package/ui/js/modal.js +79 -42
- package/ui/js/recall-lab.js +206 -60
package/ui/js/recall-lab.js
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
// SuperLocalMemory V3 — Recall Lab
|
|
1
|
+
// SuperLocalMemory V3 — Recall Lab with Dual-Display + Pagination
|
|
2
2
|
// Part of Qualixar | https://superlocalmemory.com
|
|
3
3
|
|
|
4
|
+
var recallLabState = {
|
|
5
|
+
allResults: [],
|
|
6
|
+
page: 0,
|
|
7
|
+
perPage: 10,
|
|
8
|
+
query: '',
|
|
9
|
+
synthesis: '',
|
|
10
|
+
};
|
|
11
|
+
|
|
4
12
|
document.getElementById('recall-lab-search')?.addEventListener('click', function() {
|
|
5
13
|
var query = document.getElementById('recall-lab-query').value.trim();
|
|
6
14
|
if (!query) return;
|
|
7
15
|
|
|
16
|
+
recallLabState.query = query;
|
|
17
|
+
recallLabState.page = 0;
|
|
18
|
+
var perPageEl = document.getElementById('recall-lab-per-page');
|
|
19
|
+
recallLabState.perPage = perPageEl ? parseInt(perPageEl.value) : 10;
|
|
20
|
+
var fetchLimit = Math.max(recallLabState.perPage * 5, 50);
|
|
21
|
+
|
|
8
22
|
var resultsDiv = document.getElementById('recall-lab-results');
|
|
9
23
|
var metaDiv = document.getElementById('recall-lab-meta');
|
|
10
24
|
resultsDiv.textContent = '';
|
|
11
25
|
var spinner = document.createElement('div');
|
|
12
|
-
spinner.className = 'text-center';
|
|
26
|
+
spinner.className = 'text-center py-4';
|
|
13
27
|
var spinnerInner = document.createElement('div');
|
|
14
28
|
spinnerInner.className = 'spinner-border text-primary';
|
|
15
29
|
spinner.appendChild(spinnerInner);
|
|
@@ -18,7 +32,7 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
|
|
|
18
32
|
fetch('/api/v3/recall/trace', {
|
|
19
33
|
method: 'POST',
|
|
20
34
|
headers: {'Content-Type': 'application/json'},
|
|
21
|
-
body: JSON.stringify({query: query, limit:
|
|
35
|
+
body: JSON.stringify({query: query, limit: fetchLimit, synthesize: true})
|
|
22
36
|
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
23
37
|
if (data.error) {
|
|
24
38
|
resultsDiv.textContent = '';
|
|
@@ -32,11 +46,14 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
|
|
|
32
46
|
metaDiv.textContent = '';
|
|
33
47
|
appendMetaField(metaDiv, 'Query type: ', data.query_type || 'unknown');
|
|
34
48
|
metaDiv.appendChild(document.createTextNode(' | '));
|
|
35
|
-
appendMetaField(metaDiv, 'Results: ', String(data.
|
|
49
|
+
appendMetaField(metaDiv, 'Results: ', String((data.results || []).length));
|
|
36
50
|
metaDiv.appendChild(document.createTextNode(' | '));
|
|
37
51
|
appendMetaField(metaDiv, 'Time: ', (data.retrieval_time_ms || 0).toFixed(0) + 'ms');
|
|
38
52
|
|
|
39
|
-
|
|
53
|
+
recallLabState.allResults = data.results || [];
|
|
54
|
+
recallLabState.synthesis = data.synthesis || '';
|
|
55
|
+
|
|
56
|
+
if (recallLabState.allResults.length === 0) {
|
|
40
57
|
resultsDiv.textContent = '';
|
|
41
58
|
var infoDiv = document.createElement('div');
|
|
42
59
|
infoDiv.className = 'alert alert-info';
|
|
@@ -45,69 +62,204 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
|
|
|
45
62
|
return;
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
renderRecallPage();
|
|
66
|
+
}).catch(function(e) {
|
|
48
67
|
resultsDiv.textContent = '';
|
|
49
|
-
var
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
var item = document.createElement('div');
|
|
57
|
-
item.className = 'list-group-item list-group-item-action';
|
|
58
|
-
item.style.cursor = 'pointer';
|
|
59
|
-
item.title = 'Click to view full memory';
|
|
60
|
-
(function(result) {
|
|
61
|
-
item.addEventListener('click', function() {
|
|
62
|
-
if (typeof openMemoryDetail === 'function') {
|
|
63
|
-
openMemoryDetail({
|
|
64
|
-
id: result.fact_id,
|
|
65
|
-
content: result.content,
|
|
66
|
-
score: result.score,
|
|
67
|
-
importance: Math.round((result.confidence || 0.5) * 10),
|
|
68
|
-
category: 'recall',
|
|
69
|
-
tags: Object.keys(result.channel_scores || {}).join(', '),
|
|
70
|
-
created_at: null,
|
|
71
|
-
trust_score: result.trust_score,
|
|
72
|
-
channel_scores: result.channel_scores
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
})(r);
|
|
68
|
+
var errDiv = document.createElement('div');
|
|
69
|
+
errDiv.className = 'alert alert-danger';
|
|
70
|
+
errDiv.textContent = 'Error: ' + e.message;
|
|
71
|
+
resultsDiv.appendChild(errDiv);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
item.appendChild(header);
|
|
75
|
+
function renderRecallPage() {
|
|
76
|
+
var resultsDiv = document.getElementById('recall-lab-results');
|
|
77
|
+
resultsDiv.textContent = '';
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
var results = recallLabState.allResults;
|
|
80
|
+
var start = recallLabState.page * recallLabState.perPage;
|
|
81
|
+
var end = Math.min(start + recallLabState.perPage, results.length);
|
|
82
|
+
var pageResults = results.slice(start, end);
|
|
83
|
+
var totalPages = Math.ceil(results.length / recallLabState.perPage);
|
|
87
84
|
|
|
85
|
+
// Synthesis banner (Mode B/C only)
|
|
86
|
+
if (recallLabState.synthesis && recallLabState.page === 0) {
|
|
87
|
+
var synBanner = document.createElement('div');
|
|
88
|
+
synBanner.className = 'alert alert-light border-start border-4 border-primary mb-3';
|
|
89
|
+
var synTitle = document.createElement('strong');
|
|
90
|
+
synTitle.textContent = 'AI Summary';
|
|
91
|
+
synBanner.appendChild(synTitle);
|
|
92
|
+
synBanner.appendChild(document.createElement('br'));
|
|
93
|
+
var synText = document.createElement('span');
|
|
94
|
+
synText.textContent = recallLabState.synthesis;
|
|
95
|
+
synBanner.appendChild(synText);
|
|
96
|
+
resultsDiv.appendChild(synBanner);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var listGroup = document.createElement('div');
|
|
100
|
+
listGroup.className = 'list-group';
|
|
101
|
+
|
|
102
|
+
pageResults.forEach(function(r, i) {
|
|
103
|
+
var globalIndex = start + i;
|
|
104
|
+
var channels = r.channel_scores || {};
|
|
105
|
+
var maxChannel = Math.max(channels.semantic || 0, channels.bm25 || 0, channels.entity_graph || 0, channels.temporal || 0) || 1;
|
|
106
|
+
var hasSource = r.source_content && r.source_content.length > 0;
|
|
107
|
+
var displayText = hasSource ? r.source_content : r.content;
|
|
108
|
+
|
|
109
|
+
var item = document.createElement('div');
|
|
110
|
+
item.className = 'list-group-item';
|
|
111
|
+
|
|
112
|
+
// Score badge row
|
|
113
|
+
var scoreRow = document.createElement('div');
|
|
114
|
+
scoreRow.className = 'd-flex justify-content-between align-items-center mb-1';
|
|
115
|
+
var numLabel = document.createElement('strong');
|
|
116
|
+
numLabel.textContent = '#' + (globalIndex + 1);
|
|
117
|
+
scoreRow.appendChild(numLabel);
|
|
118
|
+
var scoreBadges = document.createElement('div');
|
|
119
|
+
scoreBadges.innerHTML = '<span class="badge bg-primary me-1">Score: ' + r.score + '</span>' +
|
|
120
|
+
'<span class="badge bg-secondary me-1">Trust: ' + r.trust_score + '</span>' +
|
|
121
|
+
'<span class="badge bg-outline-info" style="border:1px solid #0dcaf0;color:#0dcaf0;">Conf: ' + r.confidence + '</span>';
|
|
122
|
+
scoreRow.appendChild(scoreBadges);
|
|
123
|
+
item.appendChild(scoreRow);
|
|
124
|
+
|
|
125
|
+
// Original memory text (primary display)
|
|
126
|
+
var contentDiv = document.createElement('div');
|
|
127
|
+
contentDiv.className = 'mb-2';
|
|
128
|
+
contentDiv.style.cssText = 'white-space:pre-wrap; line-height:1.5;';
|
|
129
|
+
var truncated = displayText.length > 500 ? displayText.substring(0, 500) + '...' : displayText;
|
|
130
|
+
contentDiv.textContent = truncated;
|
|
131
|
+
item.appendChild(contentDiv);
|
|
132
|
+
|
|
133
|
+
// Expandable atomic fact section (only if source differs from content)
|
|
134
|
+
if (hasSource && r.content !== r.source_content) {
|
|
135
|
+
var expandBtn = document.createElement('button');
|
|
136
|
+
expandBtn.className = 'btn btn-sm btn-outline-secondary mb-2';
|
|
137
|
+
expandBtn.textContent = 'Show matched fact + channels';
|
|
138
|
+
var factSection = document.createElement('div');
|
|
139
|
+
factSection.style.display = 'none';
|
|
140
|
+
factSection.className = 'border-top pt-2 mt-1';
|
|
141
|
+
|
|
142
|
+
var factLabel = document.createElement('small');
|
|
143
|
+
factLabel.className = 'text-muted d-block mb-1';
|
|
144
|
+
factLabel.textContent = 'Matched atomic fact:';
|
|
145
|
+
factSection.appendChild(factLabel);
|
|
146
|
+
|
|
147
|
+
var factContent = document.createElement('div');
|
|
148
|
+
factContent.className = 'small bg-light p-2 rounded mb-2';
|
|
149
|
+
factContent.textContent = r.content;
|
|
150
|
+
factSection.appendChild(factContent);
|
|
151
|
+
|
|
152
|
+
// Channel bars
|
|
153
|
+
factSection.appendChild(buildChannelBar('Semantic', channels.semantic || 0, maxChannel, 'primary'));
|
|
154
|
+
factSection.appendChild(buildChannelBar('BM25', channels.bm25 || 0, maxChannel, 'success'));
|
|
155
|
+
factSection.appendChild(buildChannelBar('Entity', channels.entity_graph || 0, maxChannel, 'info'));
|
|
156
|
+
factSection.appendChild(buildChannelBar('Temporal', channels.temporal || 0, maxChannel, 'warning'));
|
|
157
|
+
|
|
158
|
+
expandBtn.addEventListener('click', function() {
|
|
159
|
+
var visible = factSection.style.display !== 'none';
|
|
160
|
+
factSection.style.display = visible ? 'none' : 'block';
|
|
161
|
+
expandBtn.textContent = visible ? 'Show matched fact + channels' : 'Hide matched fact';
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
item.appendChild(expandBtn);
|
|
165
|
+
item.appendChild(factSection);
|
|
166
|
+
} else {
|
|
167
|
+
// No source_content — show channel bars inline
|
|
88
168
|
var barsDiv = document.createElement('div');
|
|
89
|
-
barsDiv.className = 'mt-
|
|
169
|
+
barsDiv.className = 'mt-1';
|
|
90
170
|
barsDiv.appendChild(buildChannelBar('Semantic', channels.semantic || 0, maxChannel, 'primary'));
|
|
91
171
|
barsDiv.appendChild(buildChannelBar('BM25', channels.bm25 || 0, maxChannel, 'success'));
|
|
92
172
|
barsDiv.appendChild(buildChannelBar('Entity', channels.entity_graph || 0, maxChannel, 'info'));
|
|
93
173
|
barsDiv.appendChild(buildChannelBar('Temporal', channels.temporal || 0, maxChannel, 'warning'));
|
|
94
174
|
item.appendChild(barsDiv);
|
|
175
|
+
}
|
|
95
176
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
177
|
+
// Click for detail modal
|
|
178
|
+
item.style.cursor = 'pointer';
|
|
179
|
+
(function(result) {
|
|
180
|
+
contentDiv.addEventListener('click', function() {
|
|
181
|
+
if (typeof openMemoryDetail === 'function') {
|
|
182
|
+
openMemoryDetail({
|
|
183
|
+
id: result.fact_id,
|
|
184
|
+
memory_id: result.memory_id,
|
|
185
|
+
content: result.source_content || result.content,
|
|
186
|
+
score: result.score,
|
|
187
|
+
importance: Math.round((result.confidence || 0.5) * 10),
|
|
188
|
+
category: 'recall',
|
|
189
|
+
created_at: null,
|
|
190
|
+
trust_score: result.trust_score,
|
|
191
|
+
channel_scores: result.channel_scores
|
|
192
|
+
}, 'recall'); // source='recall': show View Original, hide Expand Neighbors
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
})(r);
|
|
196
|
+
|
|
197
|
+
listGroup.appendChild(item);
|
|
105
198
|
});
|
|
106
|
-
|
|
199
|
+
resultsDiv.appendChild(listGroup);
|
|
200
|
+
|
|
201
|
+
// Pagination
|
|
202
|
+
if (totalPages > 1) {
|
|
203
|
+
var nav = document.createElement('nav');
|
|
204
|
+
nav.className = 'mt-3';
|
|
205
|
+
var ul = document.createElement('ul');
|
|
206
|
+
ul.className = 'pagination justify-content-center';
|
|
207
|
+
|
|
208
|
+
var prevLi = document.createElement('li');
|
|
209
|
+
prevLi.className = 'page-item' + (recallLabState.page === 0 ? ' disabled' : '');
|
|
210
|
+
var prevA = document.createElement('a');
|
|
211
|
+
prevA.className = 'page-link';
|
|
212
|
+
prevA.href = '#';
|
|
213
|
+
prevA.textContent = 'Previous';
|
|
214
|
+
prevA.addEventListener('click', function(e) {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
if (recallLabState.page > 0) { recallLabState.page--; renderRecallPage(); }
|
|
217
|
+
});
|
|
218
|
+
prevLi.appendChild(prevA);
|
|
219
|
+
ul.appendChild(prevLi);
|
|
220
|
+
|
|
221
|
+
for (var p = 0; p < totalPages; p++) {
|
|
222
|
+
var li = document.createElement('li');
|
|
223
|
+
li.className = 'page-item' + (p === recallLabState.page ? ' active' : '');
|
|
224
|
+
var a = document.createElement('a');
|
|
225
|
+
a.className = 'page-link';
|
|
226
|
+
a.href = '#';
|
|
227
|
+
a.textContent = String(p + 1);
|
|
228
|
+
(function(pageNum) {
|
|
229
|
+
a.addEventListener('click', function(e) {
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
recallLabState.page = pageNum;
|
|
232
|
+
renderRecallPage();
|
|
233
|
+
});
|
|
234
|
+
})(p);
|
|
235
|
+
li.appendChild(a);
|
|
236
|
+
ul.appendChild(li);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
var nextLi = document.createElement('li');
|
|
240
|
+
nextLi.className = 'page-item' + (recallLabState.page >= totalPages - 1 ? ' disabled' : '');
|
|
241
|
+
var nextA = document.createElement('a');
|
|
242
|
+
nextA.className = 'page-link';
|
|
243
|
+
nextA.href = '#';
|
|
244
|
+
nextA.textContent = 'Next';
|
|
245
|
+
nextA.addEventListener('click', function(e) {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
if (recallLabState.page < totalPages - 1) { recallLabState.page++; renderRecallPage(); }
|
|
248
|
+
});
|
|
249
|
+
nextLi.appendChild(nextA);
|
|
250
|
+
ul.appendChild(nextLi);
|
|
251
|
+
nav.appendChild(ul);
|
|
252
|
+
resultsDiv.appendChild(nav);
|
|
253
|
+
|
|
254
|
+
var info = document.createElement('div');
|
|
255
|
+
info.className = 'text-center text-muted small';
|
|
256
|
+
info.textContent = 'Showing ' + (start + 1) + '-' + end + ' of ' + results.length + ' results';
|
|
257
|
+
resultsDiv.appendChild(info);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
107
260
|
|
|
108
261
|
function appendMetaField(parent, label, value) {
|
|
109
|
-
|
|
110
|
-
parent.appendChild(text);
|
|
262
|
+
parent.appendChild(document.createTextNode(label));
|
|
111
263
|
var strong = document.createElement('strong');
|
|
112
264
|
strong.textContent = value;
|
|
113
265
|
parent.appendChild(strong);
|
|
@@ -115,32 +267,26 @@ function appendMetaField(parent, label, value) {
|
|
|
115
267
|
|
|
116
268
|
function buildChannelBar(name, score, max, color) {
|
|
117
269
|
var pct = max > 0 ? Math.round((score / max) * 100) : 0;
|
|
118
|
-
|
|
119
270
|
var row = document.createElement('div');
|
|
120
271
|
row.className = 'd-flex align-items-center mb-1';
|
|
121
|
-
|
|
122
272
|
var label = document.createElement('span');
|
|
123
273
|
label.className = 'me-2';
|
|
124
274
|
label.style.width = '70px';
|
|
125
275
|
label.style.fontSize = '0.75rem';
|
|
126
276
|
label.textContent = name;
|
|
127
277
|
row.appendChild(label);
|
|
128
|
-
|
|
129
278
|
var progressWrap = document.createElement('div');
|
|
130
279
|
progressWrap.className = 'progress flex-grow-1';
|
|
131
280
|
progressWrap.style.height = '14px';
|
|
132
|
-
|
|
133
281
|
var bar = document.createElement('div');
|
|
134
282
|
bar.className = 'progress-bar bg-' + color;
|
|
135
283
|
bar.style.width = pct + '%';
|
|
136
284
|
bar.textContent = score.toFixed(3);
|
|
137
285
|
progressWrap.appendChild(bar);
|
|
138
|
-
|
|
139
286
|
row.appendChild(progressWrap);
|
|
140
287
|
return row;
|
|
141
288
|
}
|
|
142
289
|
|
|
143
|
-
// Enter key support
|
|
144
290
|
document.getElementById('recall-lab-query')?.addEventListener('keydown', function(e) {
|
|
145
291
|
if (e.key === 'Enter') document.getElementById('recall-lab-search')?.click();
|
|
146
292
|
});
|