@199-bio/engram 0.7.2 → 0.8.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/dist/consolidation/consolidator.d.ts.map +1 -1
- package/dist/index.js +119 -3
- package/dist/retrieval/hybrid.d.ts.map +1 -1
- package/dist/settings.d.ts.map +1 -0
- package/dist/storage/database.d.ts.map +1 -1
- package/dist/web/chat-handler.d.ts.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/consolidation/consolidator.ts +14 -1
- package/src/index.ts +134 -3
- package/src/retrieval/hybrid.ts +152 -15
- package/src/settings.ts +73 -0
- package/src/storage/database.ts +312 -0
- package/src/web/chat-handler.ts +15 -4
- package/src/web/server.ts +49 -3
- package/src/web/static/app.js +100 -0
- package/src/web/static/index.html +35 -0
- package/src/web/static/style.css +243 -43
package/src/web/server.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { KnowledgeGraph } from "../graph/knowledge-graph.js";
|
|
|
13
13
|
import { HybridSearch } from "../retrieval/hybrid.js";
|
|
14
14
|
import { ChatHandler } from "./chat-handler.js";
|
|
15
15
|
import { Consolidator } from "../consolidation/consolidator.js";
|
|
16
|
+
import { loadSettings, saveSettings, hasAnthropicApiKey } from "../settings.js";
|
|
16
17
|
|
|
17
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
19
|
const __dirname = path.dirname(__filename);
|
|
@@ -240,9 +241,9 @@ export class EngramWebServer {
|
|
|
240
241
|
const offset = parseInt(url.searchParams.get("offset") || "0");
|
|
241
242
|
|
|
242
243
|
if (query) {
|
|
243
|
-
const
|
|
244
|
+
const response = await this.search.search(query, { limit });
|
|
244
245
|
res.end(JSON.stringify({
|
|
245
|
-
memories: results.map(r => ({
|
|
246
|
+
memories: response.results.map(r => ({
|
|
246
247
|
...r.memory,
|
|
247
248
|
score: r.score,
|
|
248
249
|
sources: r.sources,
|
|
@@ -379,13 +380,58 @@ export class EngramWebServer {
|
|
|
379
380
|
return;
|
|
380
381
|
}
|
|
381
382
|
|
|
383
|
+
// ============ Settings Endpoints ============
|
|
384
|
+
|
|
385
|
+
// GET /api/settings - get current settings (without exposing full API key)
|
|
386
|
+
if (pathname === "/api/settings" && method === "GET") {
|
|
387
|
+
const settings = loadSettings();
|
|
388
|
+
res.end(JSON.stringify({
|
|
389
|
+
has_api_key: hasAnthropicApiKey(),
|
|
390
|
+
api_key_preview: settings.anthropic_api_key
|
|
391
|
+
? `${settings.anthropic_api_key.slice(0, 12)}...${settings.anthropic_api_key.slice(-4)}`
|
|
392
|
+
: null,
|
|
393
|
+
api_key_source: settings.anthropic_api_key
|
|
394
|
+
? "settings"
|
|
395
|
+
: process.env.ANTHROPIC_API_KEY
|
|
396
|
+
? "environment"
|
|
397
|
+
: null,
|
|
398
|
+
}));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// POST /api/settings - update settings
|
|
403
|
+
if (pathname === "/api/settings" && method === "POST") {
|
|
404
|
+
const { anthropic_api_key } = body as { anthropic_api_key?: string };
|
|
405
|
+
|
|
406
|
+
if (anthropic_api_key !== undefined) {
|
|
407
|
+
const settings = loadSettings();
|
|
408
|
+
if (anthropic_api_key === "") {
|
|
409
|
+
// Clear the API key
|
|
410
|
+
delete settings.anthropic_api_key;
|
|
411
|
+
} else {
|
|
412
|
+
settings.anthropic_api_key = anthropic_api_key;
|
|
413
|
+
}
|
|
414
|
+
saveSettings(settings);
|
|
415
|
+
|
|
416
|
+
// Refresh the chat client
|
|
417
|
+
this.chat.refreshClient();
|
|
418
|
+
this.consolidator = new Consolidator(this.db); // Reinit consolidator
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
res.end(JSON.stringify({
|
|
422
|
+
success: true,
|
|
423
|
+
configured: this.chat.isConfigured(),
|
|
424
|
+
}));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
382
428
|
// GET /api/chat/status - check if chat is configured
|
|
383
429
|
if (pathname === "/api/chat/status" && method === "GET") {
|
|
384
430
|
res.end(JSON.stringify({
|
|
385
431
|
configured: this.chat.isConfigured(),
|
|
386
432
|
message: this.chat.isConfigured()
|
|
387
433
|
? "Chat is ready"
|
|
388
|
-
: "
|
|
434
|
+
: "Configure API key in Settings",
|
|
389
435
|
}));
|
|
390
436
|
return;
|
|
391
437
|
}
|
package/src/web/static/app.js
CHANGED
|
@@ -17,6 +17,7 @@ const views = {
|
|
|
17
17
|
entities: document.getElementById('entities-view'),
|
|
18
18
|
graph: document.getElementById('graph-view'),
|
|
19
19
|
consolidation: document.getElementById('consolidation-view'),
|
|
20
|
+
settings: document.getElementById('settings-view'),
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
const statsEl = document.getElementById('stats');
|
|
@@ -352,6 +353,7 @@ function switchView(view) {
|
|
|
352
353
|
if (view === 'entities') loadEntities(entityTypeFilter.value);
|
|
353
354
|
if (view === 'graph') loadGraph();
|
|
354
355
|
if (view === 'consolidation') loadConsolidation();
|
|
356
|
+
if (view === 'settings') loadSettings();
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
// ============ Consolidation ============
|
|
@@ -881,6 +883,104 @@ async function checkApiStatus() {
|
|
|
881
883
|
}
|
|
882
884
|
}
|
|
883
885
|
|
|
886
|
+
// ============ Settings ============
|
|
887
|
+
|
|
888
|
+
const apiStatusBadge = document.getElementById('api-status-badge');
|
|
889
|
+
const apiKeyInput = document.getElementById('api-key-input');
|
|
890
|
+
const toggleKeyVisibility = document.getElementById('toggle-key-visibility');
|
|
891
|
+
const saveApiKeyBtn = document.getElementById('save-api-key');
|
|
892
|
+
const clearApiKeyBtn = document.getElementById('clear-api-key');
|
|
893
|
+
|
|
894
|
+
async function loadSettings() {
|
|
895
|
+
try {
|
|
896
|
+
const settings = await api('/api/settings');
|
|
897
|
+
updateSettingsUI(settings);
|
|
898
|
+
} catch (e) {
|
|
899
|
+
console.error('Failed to load settings', e);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function updateSettingsUI(settings) {
|
|
904
|
+
if (settings.has_api_key) {
|
|
905
|
+
apiStatusBadge.textContent = `Configured (${settings.api_key_source})`;
|
|
906
|
+
apiStatusBadge.className = 'status-badge configured';
|
|
907
|
+
apiKeyInput.placeholder = settings.api_key_preview || 'sk-ant-api03-...';
|
|
908
|
+
apiKeyInput.value = '';
|
|
909
|
+
} else {
|
|
910
|
+
apiStatusBadge.textContent = 'Not configured';
|
|
911
|
+
apiStatusBadge.className = 'status-badge not-configured';
|
|
912
|
+
apiKeyInput.placeholder = 'sk-ant-api03-...';
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (toggleKeyVisibility) {
|
|
917
|
+
toggleKeyVisibility.addEventListener('click', () => {
|
|
918
|
+
const type = apiKeyInput.type === 'password' ? 'text' : 'password';
|
|
919
|
+
apiKeyInput.type = type;
|
|
920
|
+
toggleKeyVisibility.textContent = type === 'password' ? '👁' : '🙈';
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (saveApiKeyBtn) {
|
|
925
|
+
saveApiKeyBtn.addEventListener('click', async () => {
|
|
926
|
+
const apiKey = apiKeyInput.value.trim();
|
|
927
|
+
if (!apiKey) {
|
|
928
|
+
alert('Please enter an API key');
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (!apiKey.startsWith('sk-ant-')) {
|
|
933
|
+
alert('Invalid API key format. Should start with sk-ant-');
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
try {
|
|
938
|
+
saveApiKeyBtn.disabled = true;
|
|
939
|
+
saveApiKeyBtn.textContent = 'Saving...';
|
|
940
|
+
|
|
941
|
+
const result = await api('/api/settings', {
|
|
942
|
+
method: 'POST',
|
|
943
|
+
body: { anthropic_api_key: apiKey },
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
if (result.success) {
|
|
947
|
+
apiKeyInput.value = '';
|
|
948
|
+
await loadSettings();
|
|
949
|
+
await checkApiStatus();
|
|
950
|
+
alert('API key saved successfully!');
|
|
951
|
+
} else {
|
|
952
|
+
alert('Failed to save API key');
|
|
953
|
+
}
|
|
954
|
+
} catch (e) {
|
|
955
|
+
console.error('Failed to save API key', e);
|
|
956
|
+
alert('Error saving API key');
|
|
957
|
+
} finally {
|
|
958
|
+
saveApiKeyBtn.disabled = false;
|
|
959
|
+
saveApiKeyBtn.textContent = 'Save API Key';
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (clearApiKeyBtn) {
|
|
965
|
+
clearApiKeyBtn.addEventListener('click', async () => {
|
|
966
|
+
if (!confirm('Are you sure you want to clear the API key?')) return;
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
clearApiKeyBtn.disabled = true;
|
|
970
|
+
await api('/api/settings', {
|
|
971
|
+
method: 'POST',
|
|
972
|
+
body: { anthropic_api_key: '' },
|
|
973
|
+
});
|
|
974
|
+
await loadSettings();
|
|
975
|
+
await checkApiStatus();
|
|
976
|
+
} catch (e) {
|
|
977
|
+
console.error('Failed to clear API key', e);
|
|
978
|
+
} finally {
|
|
979
|
+
clearApiKeyBtn.disabled = false;
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
884
984
|
// Initialize
|
|
885
985
|
checkApiStatus();
|
|
886
986
|
loadStats();
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<button class="nav-btn" data-view="entities">Entities</button>
|
|
15
15
|
<button class="nav-btn" data-view="graph">Graph</button>
|
|
16
16
|
<button class="nav-btn" data-view="consolidation">Consolidation</button>
|
|
17
|
+
<button class="nav-btn" data-view="settings">Settings</button>
|
|
17
18
|
</nav>
|
|
18
19
|
<div class="stats" id="stats"></div>
|
|
19
20
|
<div class="api-status" id="api-status" title="Anthropic API Status">
|
|
@@ -59,6 +60,40 @@
|
|
|
59
60
|
<div id="graph-container"></div>
|
|
60
61
|
</section>
|
|
61
62
|
|
|
63
|
+
<!-- Settings View -->
|
|
64
|
+
<section id="settings-view" class="view">
|
|
65
|
+
<div class="settings-container">
|
|
66
|
+
<h2>Settings</h2>
|
|
67
|
+
|
|
68
|
+
<div class="settings-section">
|
|
69
|
+
<h3>API Configuration</h3>
|
|
70
|
+
<p class="section-desc">Configure your Anthropic API key to enable Chat and Consolidation features.</p>
|
|
71
|
+
|
|
72
|
+
<div class="settings-row" id="api-key-status">
|
|
73
|
+
<label>Status:</label>
|
|
74
|
+
<span class="status-badge" id="api-status-badge">Checking...</span>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="settings-row">
|
|
78
|
+
<label for="api-key-input">API Key:</label>
|
|
79
|
+
<div class="input-group">
|
|
80
|
+
<input type="password" id="api-key-input" placeholder="sk-ant-api03-..." autocomplete="off">
|
|
81
|
+
<button type="button" id="toggle-key-visibility" title="Show/hide">👁</button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="settings-row">
|
|
86
|
+
<button id="save-api-key" class="primary-btn">Save API Key</button>
|
|
87
|
+
<button id="clear-api-key" class="danger-btn">Clear</button>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<p class="help-text">
|
|
91
|
+
Get your API key from <a href="https://console.anthropic.com/settings/keys" target="_blank">console.anthropic.com</a>
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</section>
|
|
96
|
+
|
|
62
97
|
<!-- Consolidation View -->
|
|
63
98
|
<section id="consolidation-view" class="view">
|
|
64
99
|
<div class="consolidation-header">
|