superlocalmemory 3.4.17 → 3.4.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/CHANGELOG.md +8 -0
- package/package.json +1 -3
- package/pyproject.toml +10 -1
- package/src/superlocalmemory/cli/setup_wizard.py +30 -0
- package/src/superlocalmemory.egg-info/PKG-INFO +4 -1
- package/src/superlocalmemory.egg-info/requires.txt +3 -0
- package/docs/ARCHITECTURE.md +0 -149
- package/docs/api-reference.md +0 -284
- package/docs/auto-memory.md +0 -150
- package/docs/cli-reference.md +0 -327
- package/docs/cloud-backup.md +0 -174
- package/docs/compliance.md +0 -191
- package/docs/configuration.md +0 -182
- package/docs/getting-started.md +0 -102
- package/docs/ide-setup.md +0 -261
- package/docs/mcp-tools.md +0 -220
- package/docs/migration-from-v2.md +0 -170
- package/docs/profiles.md +0 -173
- package/docs/screenshots/01-dashboard-main.png +0 -0
- package/docs/screenshots/02-knowledge-graph.png +0 -0
- package/docs/screenshots/03-math-health.png +0 -0
- package/docs/screenshots/03-patterns-learning.png +0 -0
- package/docs/screenshots/04-learning-dashboard.png +0 -0
- package/docs/screenshots/04-recall-lab.png +0 -0
- package/docs/screenshots/05-behavioral-analysis.png +0 -0
- package/docs/screenshots/05-trust-dashboard.png +0 -0
- package/docs/screenshots/06-graph-communities.png +0 -0
- package/docs/screenshots/06-settings.png +0 -0
- package/docs/screenshots/07-memories-blurred.png +0 -0
- package/docs/skill-evolution.md +0 -256
- package/docs/troubleshooting.md +0 -310
- package/docs/v2-archive/ACCESSIBILITY.md +0 -291
- package/docs/v2-archive/ARCHITECTURE.md +0 -886
- package/docs/v2-archive/CLI-COMMANDS-REFERENCE.md +0 -425
- package/docs/v2-archive/COMPRESSION-README.md +0 -390
- package/docs/v2-archive/FRAMEWORK-INTEGRATIONS.md +0 -300
- package/docs/v2-archive/MCP-MANUAL-SETUP.md +0 -775
- package/docs/v2-archive/MCP-TROUBLESHOOTING.md +0 -787
- package/docs/v2-archive/PATTERN-LEARNING.md +0 -228
- package/docs/v2-archive/PROFILES-GUIDE.md +0 -453
- package/docs/v2-archive/RESET-GUIDE.md +0 -353
- package/docs/v2-archive/SEARCH-ENGINE-V2.2.0.md +0 -749
- package/docs/v2-archive/SEARCH-INTEGRATION-GUIDE.md +0 -502
- package/docs/v2-archive/UI-SERVER.md +0 -262
- package/docs/v2-archive/UNIVERSAL-INTEGRATION.md +0 -488
- package/docs/v2-archive/V2.2.0-OPTIONAL-SEARCH.md +0 -666
- package/docs/v2-archive/WINDOWS-INSTALL-README.txt +0 -34
- package/docs/v2-archive/WINDOWS-POST-INSTALL.txt +0 -45
- package/docs/v2-archive/example_graph_usage.py +0 -146
- package/ui/index.html +0 -1879
- package/ui/js/agents.js +0 -192
- package/ui/js/auto-settings.js +0 -399
- package/ui/js/behavioral.js +0 -276
- package/ui/js/clusters.js +0 -206
- package/ui/js/compliance.js +0 -252
- package/ui/js/core.js +0 -246
- package/ui/js/dashboard.js +0 -110
- package/ui/js/events.js +0 -178
- package/ui/js/fact-detail.js +0 -92
- package/ui/js/feedback.js +0 -333
- package/ui/js/graph-core.js +0 -447
- package/ui/js/graph-filters.js +0 -220
- package/ui/js/graph-interactions.js +0 -351
- package/ui/js/graph-ui.js +0 -214
- package/ui/js/ide-status.js +0 -102
- package/ui/js/init.js +0 -45
- package/ui/js/learning.js +0 -435
- package/ui/js/lifecycle.js +0 -298
- package/ui/js/math-health.js +0 -98
- package/ui/js/memories.js +0 -264
- package/ui/js/modal.js +0 -357
- package/ui/js/patterns.js +0 -93
- package/ui/js/profiles.js +0 -236
- package/ui/js/recall-lab.js +0 -292
- package/ui/js/search.js +0 -59
- package/ui/js/settings.js +0 -224
- package/ui/js/timeline.js +0 -32
- package/ui/js/trust-dashboard.js +0 -73
package/ui/js/agents.js
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
// SuperLocalMemory V2 - Connected Agents + Trust Overview (v2.5)
|
|
2
|
-
// Depends on: core.js
|
|
3
|
-
// Security: All DOM built with safe methods (createElement/textContent).
|
|
4
|
-
|
|
5
|
-
async function loadAgents() {
|
|
6
|
-
try {
|
|
7
|
-
var response = await fetch('/api/agents');
|
|
8
|
-
var data = await response.json();
|
|
9
|
-
var agents = data.agents || [];
|
|
10
|
-
var stats = data.stats || {};
|
|
11
|
-
|
|
12
|
-
var el;
|
|
13
|
-
el = document.getElementById('agent-stat-total');
|
|
14
|
-
if (el) el.textContent = (stats.total_agents || 0).toLocaleString();
|
|
15
|
-
el = document.getElementById('agent-stat-active');
|
|
16
|
-
if (el) el.textContent = (stats.active_last_24h || 0).toLocaleString();
|
|
17
|
-
el = document.getElementById('agent-stat-writes');
|
|
18
|
-
if (el) el.textContent = (stats.total_writes || 0).toLocaleString();
|
|
19
|
-
el = document.getElementById('agent-stat-recalls');
|
|
20
|
-
if (el) el.textContent = (stats.total_recalls || 0).toLocaleString();
|
|
21
|
-
|
|
22
|
-
var container = document.getElementById('agents-list');
|
|
23
|
-
if (!container) return;
|
|
24
|
-
|
|
25
|
-
if (agents.length === 0) {
|
|
26
|
-
container.textContent = '';
|
|
27
|
-
var empty = document.createElement('div');
|
|
28
|
-
empty.className = 'text-muted text-center py-4';
|
|
29
|
-
var emptyIcon = document.createElement('i');
|
|
30
|
-
emptyIcon.className = 'bi bi-robot';
|
|
31
|
-
emptyIcon.style.fontSize = '2rem';
|
|
32
|
-
empty.appendChild(emptyIcon);
|
|
33
|
-
var emptyText = document.createElement('p');
|
|
34
|
-
emptyText.className = 'mt-2';
|
|
35
|
-
emptyText.textContent = 'No agents registered yet. Agents appear automatically when they connect via MCP, CLI, or REST.';
|
|
36
|
-
empty.appendChild(emptyText);
|
|
37
|
-
container.appendChild(empty);
|
|
38
|
-
loadTrustOverview();
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var table = document.createElement('table');
|
|
43
|
-
table.className = 'table table-hover table-sm';
|
|
44
|
-
var thead = document.createElement('thead');
|
|
45
|
-
var headerRow = document.createElement('tr');
|
|
46
|
-
['Agent', 'Protocol', 'Trust', 'Writes', 'Recalls', 'Last Seen'].forEach(function(h) {
|
|
47
|
-
var th = document.createElement('th');
|
|
48
|
-
th.textContent = h;
|
|
49
|
-
headerRow.appendChild(th);
|
|
50
|
-
});
|
|
51
|
-
thead.appendChild(headerRow);
|
|
52
|
-
table.appendChild(thead);
|
|
53
|
-
|
|
54
|
-
var tbody = document.createElement('tbody');
|
|
55
|
-
agents.forEach(function(agent) {
|
|
56
|
-
var tr = document.createElement('tr');
|
|
57
|
-
|
|
58
|
-
var tdName = document.createElement('td');
|
|
59
|
-
var strong = document.createElement('strong');
|
|
60
|
-
strong.textContent = agent.agent_name || agent.agent_id;
|
|
61
|
-
tdName.appendChild(strong);
|
|
62
|
-
tdName.appendChild(document.createElement('br'));
|
|
63
|
-
var smallId = document.createElement('small');
|
|
64
|
-
smallId.className = 'text-muted';
|
|
65
|
-
smallId.textContent = agent.agent_id;
|
|
66
|
-
tdName.appendChild(smallId);
|
|
67
|
-
tr.appendChild(tdName);
|
|
68
|
-
|
|
69
|
-
var tdProto = document.createElement('td');
|
|
70
|
-
var protoBadge = document.createElement('span');
|
|
71
|
-
var protocolColors = {
|
|
72
|
-
'mcp': 'bg-primary', 'cli': 'bg-success', 'rest': 'bg-info',
|
|
73
|
-
'python': 'bg-secondary'
|
|
74
|
-
};
|
|
75
|
-
protoBadge.className = 'badge ' + (protocolColors[agent.protocol] || 'bg-secondary');
|
|
76
|
-
protoBadge.textContent = agent.protocol;
|
|
77
|
-
tdProto.appendChild(protoBadge);
|
|
78
|
-
tr.appendChild(tdProto);
|
|
79
|
-
|
|
80
|
-
var tdTrust = document.createElement('td');
|
|
81
|
-
var trustScore = agent.trust_score != null ? agent.trust_score : 0.667;
|
|
82
|
-
tdTrust.className = trustScore < 0.3 ? 'text-danger fw-bold'
|
|
83
|
-
: trustScore < 0.5 ? 'text-warning fw-bold' : 'text-success fw-bold';
|
|
84
|
-
tdTrust.textContent = trustScore.toFixed(2);
|
|
85
|
-
tr.appendChild(tdTrust);
|
|
86
|
-
|
|
87
|
-
var tdW = document.createElement('td');
|
|
88
|
-
tdW.textContent = agent.memories_written || 0;
|
|
89
|
-
tr.appendChild(tdW);
|
|
90
|
-
|
|
91
|
-
var tdR = document.createElement('td');
|
|
92
|
-
tdR.textContent = agent.memories_recalled || 0;
|
|
93
|
-
tr.appendChild(tdR);
|
|
94
|
-
|
|
95
|
-
var tdLast = document.createElement('td');
|
|
96
|
-
var lastSmall = document.createElement('small');
|
|
97
|
-
lastSmall.textContent = agent.last_seen ? new Date(agent.last_seen).toLocaleString() : 'Never';
|
|
98
|
-
tdLast.appendChild(lastSmall);
|
|
99
|
-
tr.appendChild(tdLast);
|
|
100
|
-
|
|
101
|
-
tbody.appendChild(tr);
|
|
102
|
-
});
|
|
103
|
-
table.appendChild(tbody);
|
|
104
|
-
|
|
105
|
-
container.textContent = '';
|
|
106
|
-
container.appendChild(table);
|
|
107
|
-
|
|
108
|
-
loadTrustOverview();
|
|
109
|
-
|
|
110
|
-
} catch (err) {
|
|
111
|
-
console.log('Agents not available:', err);
|
|
112
|
-
var container = document.getElementById('agents-list');
|
|
113
|
-
if (container) {
|
|
114
|
-
container.textContent = '';
|
|
115
|
-
var msg = document.createElement('small');
|
|
116
|
-
msg.className = 'text-muted';
|
|
117
|
-
msg.textContent = 'Agent registry not available. This feature requires v2.5+.';
|
|
118
|
-
container.appendChild(msg);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function loadTrustOverview() {
|
|
124
|
-
try {
|
|
125
|
-
var response = await fetch('/api/trust/stats');
|
|
126
|
-
var stats = await response.json();
|
|
127
|
-
var container = document.getElementById('trust-overview');
|
|
128
|
-
if (!container) return;
|
|
129
|
-
|
|
130
|
-
container.textContent = '';
|
|
131
|
-
var row = document.createElement('div');
|
|
132
|
-
row.className = 'row g-3';
|
|
133
|
-
|
|
134
|
-
var cardData = [
|
|
135
|
-
{ value: (stats.total_signals || 0).toLocaleString(), label: 'Total Signals Collected', cls: '' },
|
|
136
|
-
{ value: (stats.avg_trust_score || 0.667).toFixed(3), label: 'Average Trust Score', cls: '' },
|
|
137
|
-
{ value: stats.enforcement || 'disabled', label: 'Enforcement Status', cls: 'text-info' }
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
cardData.forEach(function(c) {
|
|
141
|
-
var col = document.createElement('div');
|
|
142
|
-
col.className = 'col-md-4';
|
|
143
|
-
var card = document.createElement('div');
|
|
144
|
-
card.className = 'border rounded p-3 text-center';
|
|
145
|
-
var val = document.createElement('div');
|
|
146
|
-
val.className = 'fs-4 fw-bold ' + c.cls;
|
|
147
|
-
val.textContent = c.value;
|
|
148
|
-
card.appendChild(val);
|
|
149
|
-
var lbl = document.createElement('small');
|
|
150
|
-
lbl.className = 'text-muted';
|
|
151
|
-
lbl.textContent = c.label;
|
|
152
|
-
card.appendChild(lbl);
|
|
153
|
-
col.appendChild(card);
|
|
154
|
-
row.appendChild(col);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
container.appendChild(row);
|
|
158
|
-
|
|
159
|
-
if (stats.by_signal_type && Object.keys(stats.by_signal_type).length > 0) {
|
|
160
|
-
var breakdownDiv = document.createElement('div');
|
|
161
|
-
breakdownDiv.className = 'col-12 mt-3';
|
|
162
|
-
var h6 = document.createElement('h6');
|
|
163
|
-
h6.textContent = 'Signal Breakdown';
|
|
164
|
-
breakdownDiv.appendChild(h6);
|
|
165
|
-
var badgeWrap = document.createElement('div');
|
|
166
|
-
badgeWrap.className = 'd-flex flex-wrap gap-2';
|
|
167
|
-
Object.keys(stats.by_signal_type).forEach(function(type) {
|
|
168
|
-
var count = stats.by_signal_type[type];
|
|
169
|
-
var signalClass = (type.indexOf('high_volume') >= 0 || type.indexOf('quick_delete') >= 0)
|
|
170
|
-
? 'bg-danger' : (type.indexOf('recalled') >= 0 || type.indexOf('high_importance') >= 0)
|
|
171
|
-
? 'bg-success' : 'bg-secondary';
|
|
172
|
-
var b = document.createElement('span');
|
|
173
|
-
b.className = 'badge ' + signalClass;
|
|
174
|
-
b.textContent = type + ': ' + count;
|
|
175
|
-
badgeWrap.appendChild(b);
|
|
176
|
-
});
|
|
177
|
-
breakdownDiv.appendChild(badgeWrap);
|
|
178
|
-
container.appendChild(breakdownDiv);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
} catch (err) {
|
|
182
|
-
console.log('Trust stats not available:', err);
|
|
183
|
-
var container = document.getElementById('trust-overview');
|
|
184
|
-
if (container) {
|
|
185
|
-
container.textContent = '';
|
|
186
|
-
var msg = document.createElement('small');
|
|
187
|
-
msg.className = 'text-muted';
|
|
188
|
-
msg.textContent = 'Trust scoring data will appear here once agents interact with memory.';
|
|
189
|
-
container.appendChild(msg);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
package/ui/js/auto-settings.js
DELETED
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
// SuperLocalMemory V3 — Auto-Capture/Recall Settings
|
|
2
|
-
// Wires the auto-capture and auto-recall toggle switches to the V3 API.
|
|
3
|
-
|
|
4
|
-
async function loadAutoSettings() {
|
|
5
|
-
try {
|
|
6
|
-
var captureResp = await fetch('/api/v3/auto-capture/config');
|
|
7
|
-
var recallResp = await fetch('/api/v3/auto-recall/config');
|
|
8
|
-
var capture = captureResp.ok ? await captureResp.json() : {};
|
|
9
|
-
var recall = recallResp.ok ? await recallResp.json() : {};
|
|
10
|
-
|
|
11
|
-
var cc = capture.config || {};
|
|
12
|
-
var rc = recall.config || {};
|
|
13
|
-
|
|
14
|
-
var el;
|
|
15
|
-
el = document.getElementById('auto-capture-toggle');
|
|
16
|
-
if (el) el.checked = cc.enabled !== false;
|
|
17
|
-
el = document.getElementById('auto-capture-decisions');
|
|
18
|
-
if (el) el.checked = cc.capture_decisions !== false;
|
|
19
|
-
el = document.getElementById('auto-capture-bugs');
|
|
20
|
-
if (el) el.checked = cc.capture_bugs !== false;
|
|
21
|
-
el = document.getElementById('auto-recall-toggle');
|
|
22
|
-
if (el) el.checked = rc.enabled !== false;
|
|
23
|
-
el = document.getElementById('auto-recall-session');
|
|
24
|
-
if (el) el.checked = rc.on_session_start !== false;
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.log('Auto settings load error:', e);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function saveAutoCaptureConfig() {
|
|
31
|
-
var payload = {
|
|
32
|
-
enabled: document.getElementById('auto-capture-toggle')?.checked,
|
|
33
|
-
capture_decisions: document.getElementById('auto-capture-decisions')?.checked,
|
|
34
|
-
capture_bugs: document.getElementById('auto-capture-bugs')?.checked
|
|
35
|
-
};
|
|
36
|
-
fetch('/api/v3/auto-capture/config', {
|
|
37
|
-
method: 'PUT',
|
|
38
|
-
headers: { 'Content-Type': 'application/json' },
|
|
39
|
-
body: JSON.stringify(payload)
|
|
40
|
-
}).catch(function(e) { console.log('Save auto-capture error:', e); });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function saveAutoRecallConfig() {
|
|
44
|
-
var payload = {
|
|
45
|
-
enabled: document.getElementById('auto-recall-toggle')?.checked,
|
|
46
|
-
on_session_start: document.getElementById('auto-recall-session')?.checked
|
|
47
|
-
};
|
|
48
|
-
fetch('/api/v3/auto-recall/config', {
|
|
49
|
-
method: 'PUT',
|
|
50
|
-
headers: { 'Content-Type': 'application/json' },
|
|
51
|
-
body: JSON.stringify(payload)
|
|
52
|
-
}).catch(function(e) { console.log('Save auto-recall error:', e); });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Bind change listeners for auto-capture toggles
|
|
56
|
-
document.querySelectorAll('#auto-capture-toggle, #auto-capture-decisions, #auto-capture-bugs').forEach(function(el) {
|
|
57
|
-
if (el) {
|
|
58
|
-
el.addEventListener('change', saveAutoCaptureConfig);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Bind change listeners for auto-recall toggles
|
|
63
|
-
document.querySelectorAll('#auto-recall-toggle, #auto-recall-session').forEach(function(el) {
|
|
64
|
-
if (el) {
|
|
65
|
-
el.addEventListener('change', saveAutoRecallConfig);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// ============================================================================
|
|
70
|
-
// Mode / Provider / Model Configuration (Professional Settings)
|
|
71
|
-
// ============================================================================
|
|
72
|
-
|
|
73
|
-
var PROVIDER_CONFIG = {
|
|
74
|
-
'ollama': {
|
|
75
|
-
name: 'Ollama',
|
|
76
|
-
needsKey: false,
|
|
77
|
-
endpoint: 'http://localhost:11434',
|
|
78
|
-
endpointEditable: true,
|
|
79
|
-
detectModels: true, // auto-detect via /api/v3/ollama/status
|
|
80
|
-
},
|
|
81
|
-
'openrouter': {
|
|
82
|
-
name: 'OpenRouter',
|
|
83
|
-
needsKey: true,
|
|
84
|
-
endpoint: 'https://openrouter.ai/api/v1',
|
|
85
|
-
endpointEditable: false,
|
|
86
|
-
},
|
|
87
|
-
'openai': {
|
|
88
|
-
name: 'OpenAI',
|
|
89
|
-
needsKey: true,
|
|
90
|
-
endpoint: 'https://api.openai.com/v1',
|
|
91
|
-
endpointEditable: true, // editable for Azure OpenAI
|
|
92
|
-
},
|
|
93
|
-
'anthropic': {
|
|
94
|
-
name: 'Anthropic',
|
|
95
|
-
needsKey: true,
|
|
96
|
-
endpoint: 'https://api.anthropic.com',
|
|
97
|
-
endpointEditable: false,
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
var MODEL_OPTIONS = {
|
|
102
|
-
'none': [],
|
|
103
|
-
'ollama': [
|
|
104
|
-
{value: 'llama3.1:8b', label: 'Llama 3.1 8B'},
|
|
105
|
-
{value: 'llama3.2:latest', label: 'Llama 3.2'},
|
|
106
|
-
{value: 'qwen3-vl:8b', label: 'Qwen3 VL 8B'},
|
|
107
|
-
{value: 'mistral:latest', label: 'Mistral'},
|
|
108
|
-
],
|
|
109
|
-
'openrouter': [
|
|
110
|
-
{value: 'meta-llama/llama-3.1-8b-instruct:free', label: 'Llama 3.1 8B (Free)'},
|
|
111
|
-
{value: 'google/gemini-2.0-flash-001', label: 'Gemini 2.0 Flash'},
|
|
112
|
-
{value: 'anthropic/claude-3.5-haiku', label: 'Claude 3.5 Haiku'},
|
|
113
|
-
{value: 'openai/gpt-4o-mini', label: 'GPT-4o Mini'},
|
|
114
|
-
{value: 'deepseek/deepseek-chat-v3-0324:free', label: 'DeepSeek V3 (Free)'},
|
|
115
|
-
],
|
|
116
|
-
'openai': [
|
|
117
|
-
{value: 'gpt-4o-mini', label: 'GPT-4o Mini'},
|
|
118
|
-
{value: 'gpt-4o', label: 'GPT-4o'},
|
|
119
|
-
{value: 'gpt-4-turbo', label: 'GPT-4 Turbo'},
|
|
120
|
-
],
|
|
121
|
-
'anthropic': [
|
|
122
|
-
{value: 'claude-3-5-haiku-latest', label: 'Claude 3.5 Haiku'},
|
|
123
|
-
{value: 'claude-3-5-sonnet-latest', label: 'Claude 3.5 Sonnet'},
|
|
124
|
-
{value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6'},
|
|
125
|
-
],
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
async function loadModeSettings() {
|
|
129
|
-
try {
|
|
130
|
-
var resp = await fetch('/api/v3/mode');
|
|
131
|
-
if (!resp.ok) return;
|
|
132
|
-
var data = await resp.json();
|
|
133
|
-
var mode = data.mode || 'a';
|
|
134
|
-
var provider = data.provider || 'none';
|
|
135
|
-
var model = data.model || '';
|
|
136
|
-
|
|
137
|
-
// Set radio button
|
|
138
|
-
var radio = document.getElementById('mode-' + mode + '-radio');
|
|
139
|
-
if (radio) radio.checked = true;
|
|
140
|
-
|
|
141
|
-
// Set provider dropdown
|
|
142
|
-
var provEl = document.getElementById('settings-provider');
|
|
143
|
-
if (provEl && provider !== 'none') provEl.value = provider;
|
|
144
|
-
|
|
145
|
-
// Update banner
|
|
146
|
-
var modeNames = {a: 'Mode A — Local Guardian', b: 'Mode B — Smart Local', c: 'Mode C — Full Power'};
|
|
147
|
-
var bannerMode = document.getElementById('settings-current-mode');
|
|
148
|
-
if (bannerMode) {
|
|
149
|
-
var label = modeNames[mode] || mode;
|
|
150
|
-
if (provider && provider !== 'none') label += ' | ' + provider;
|
|
151
|
-
if (model) label += ' | ' + model;
|
|
152
|
-
bannerMode.textContent = label;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
var bannerDetail = document.getElementById('settings-current-detail');
|
|
156
|
-
if (bannerDetail) {
|
|
157
|
-
if (mode === 'a') bannerDetail.textContent = 'Zero cloud — EU AI Act compliant';
|
|
158
|
-
else if (data.has_key) bannerDetail.textContent = 'API key configured';
|
|
159
|
-
else if (provider === 'ollama') bannerDetail.textContent = 'No API key needed';
|
|
160
|
-
else bannerDetail.textContent = 'API key not set';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
var banner = document.getElementById('settings-current-banner');
|
|
164
|
-
if (banner) {
|
|
165
|
-
banner.className = mode === 'a' ? 'alert alert-success mb-3' :
|
|
166
|
-
mode === 'b' ? 'alert alert-info mb-3' :
|
|
167
|
-
'alert alert-warning mb-3';
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Show provider panel and populate model dropdown
|
|
171
|
-
updateModeUI();
|
|
172
|
-
|
|
173
|
-
// After provider UI updates, set the saved model value
|
|
174
|
-
if (model) {
|
|
175
|
-
setTimeout(function() {
|
|
176
|
-
var modelEl = document.getElementById('settings-model');
|
|
177
|
-
if (modelEl) {
|
|
178
|
-
// Check if option exists, if not add it
|
|
179
|
-
var found = false;
|
|
180
|
-
for (var i = 0; i < modelEl.options.length; i++) {
|
|
181
|
-
if (modelEl.options[i].value === model) { found = true; break; }
|
|
182
|
-
}
|
|
183
|
-
if (!found) {
|
|
184
|
-
var opt = document.createElement('option');
|
|
185
|
-
opt.value = model;
|
|
186
|
-
opt.textContent = model + ' (current)';
|
|
187
|
-
modelEl.insertBefore(opt, modelEl.firstChild);
|
|
188
|
-
}
|
|
189
|
-
modelEl.value = model;
|
|
190
|
-
}
|
|
191
|
-
}, 500);
|
|
192
|
-
}
|
|
193
|
-
} catch (e) {
|
|
194
|
-
console.log('Load mode settings error:', e);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function updateModeUI() {
|
|
199
|
-
var mode = document.querySelector('input[name="settings-mode-radio"]:checked')?.value || 'a';
|
|
200
|
-
var panel = document.getElementById('settings-provider-panel');
|
|
201
|
-
if (panel) {
|
|
202
|
-
panel.style.display = (mode === 'a') ? 'none' : 'block';
|
|
203
|
-
}
|
|
204
|
-
// Only set provider if it's currently empty (first load or Mode A→B/C)
|
|
205
|
-
var providerEl = document.getElementById('settings-provider');
|
|
206
|
-
if (providerEl && !providerEl.value) {
|
|
207
|
-
if (mode === 'b') providerEl.value = 'ollama';
|
|
208
|
-
}
|
|
209
|
-
if (mode !== 'a') updateProviderUI();
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function updateProviderUI() {
|
|
213
|
-
var provider = document.getElementById('settings-provider')?.value || 'none';
|
|
214
|
-
var modelSelect = document.getElementById('settings-model');
|
|
215
|
-
var modelHint = document.getElementById('settings-model-hint');
|
|
216
|
-
|
|
217
|
-
// Preserve current model before rebuilding dropdown
|
|
218
|
-
var currentModel = modelSelect ? modelSelect.value : '';
|
|
219
|
-
|
|
220
|
-
var cfg = PROVIDER_CONFIG[provider] || {};
|
|
221
|
-
|
|
222
|
-
// Show/hide API key column
|
|
223
|
-
var keyCol = document.getElementById('settings-key-col');
|
|
224
|
-
if (keyCol) keyCol.style.display = cfg.needsKey ? 'block' : 'none';
|
|
225
|
-
|
|
226
|
-
// Show/hide endpoint row
|
|
227
|
-
var endpointRow = document.getElementById('settings-endpoint-row');
|
|
228
|
-
var endpointInput = document.getElementById('settings-endpoint');
|
|
229
|
-
if (endpointRow) {
|
|
230
|
-
endpointRow.style.display = cfg.endpointEditable ? 'block' : 'none';
|
|
231
|
-
if (endpointInput && cfg.endpoint) endpointInput.value = cfg.endpoint;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// For Ollama: check live status and populate real models
|
|
235
|
-
if (provider === 'ollama') {
|
|
236
|
-
if (modelHint) modelHint.textContent = 'Checking Ollama...';
|
|
237
|
-
fetch('/api/v3/ollama/status').then(function(r) { return r.json(); }).then(function(data) {
|
|
238
|
-
if (modelSelect) {
|
|
239
|
-
modelSelect.textContent = '';
|
|
240
|
-
if (data.running && data.models.length > 0) {
|
|
241
|
-
data.models.forEach(function(m) {
|
|
242
|
-
var opt = document.createElement('option');
|
|
243
|
-
opt.value = m.name;
|
|
244
|
-
opt.textContent = m.name;
|
|
245
|
-
modelSelect.appendChild(opt);
|
|
246
|
-
});
|
|
247
|
-
if (modelHint) modelHint.textContent = 'Ollama running (' + data.count + ' models)';
|
|
248
|
-
if (modelHint) modelHint.className = 'text-success small';
|
|
249
|
-
} else {
|
|
250
|
-
var opt = document.createElement('option');
|
|
251
|
-
opt.value = '';
|
|
252
|
-
opt.textContent = 'Ollama not running!';
|
|
253
|
-
modelSelect.appendChild(opt);
|
|
254
|
-
if (modelHint) modelHint.textContent = 'Ollama not detected. Run: ollama serve';
|
|
255
|
-
if (modelHint) modelHint.className = 'text-danger small';
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}).catch(function() {
|
|
259
|
-
if (modelHint) { modelHint.textContent = 'Ollama not reachable'; modelHint.className = 'text-danger small'; }
|
|
260
|
-
});
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// For other providers: use static model list
|
|
265
|
-
if (modelSelect) {
|
|
266
|
-
modelSelect.textContent = '';
|
|
267
|
-
var options = MODEL_OPTIONS[provider] || [];
|
|
268
|
-
if (options.length === 0) {
|
|
269
|
-
var opt = document.createElement('option');
|
|
270
|
-
opt.value = '';
|
|
271
|
-
opt.textContent = 'N/A (Mode A)';
|
|
272
|
-
modelSelect.appendChild(opt);
|
|
273
|
-
} else {
|
|
274
|
-
options.forEach(function(o) {
|
|
275
|
-
var opt = document.createElement('option');
|
|
276
|
-
opt.value = o.value;
|
|
277
|
-
opt.textContent = o.label;
|
|
278
|
-
modelSelect.appendChild(opt);
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
// Restore previous model selection if it exists in the new list
|
|
282
|
-
if (currentModel) {
|
|
283
|
-
var found = false;
|
|
284
|
-
for (var i = 0; i < modelSelect.options.length; i++) {
|
|
285
|
-
if (modelSelect.options[i].value === currentModel) { found = true; break; }
|
|
286
|
-
}
|
|
287
|
-
if (found) {
|
|
288
|
-
modelSelect.value = currentModel;
|
|
289
|
-
} else if (currentModel) {
|
|
290
|
-
// Model not in list — add it as custom option so user doesn't lose their choice
|
|
291
|
-
var custom = document.createElement('option');
|
|
292
|
-
custom.value = currentModel;
|
|
293
|
-
custom.textContent = currentModel + ' (saved)';
|
|
294
|
-
modelSelect.insertBefore(custom, modelSelect.firstChild);
|
|
295
|
-
modelSelect.value = currentModel;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Update hint
|
|
301
|
-
if (modelHint) {
|
|
302
|
-
var hints = {
|
|
303
|
-
'none': 'No LLM needed in Mode A',
|
|
304
|
-
'openrouter': '200+ models via OpenRouter API',
|
|
305
|
-
'openai': 'OpenAI models (requires API key)',
|
|
306
|
-
'anthropic': 'Anthropic models (requires API key)',
|
|
307
|
-
};
|
|
308
|
-
modelHint.textContent = hints[provider] || '';
|
|
309
|
-
modelHint.className = 'text-muted small';
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async function testConnection() {
|
|
314
|
-
var provider = document.getElementById('settings-provider')?.value || '';
|
|
315
|
-
var model = document.getElementById('settings-model')?.value || '';
|
|
316
|
-
var apiKey = document.getElementById('settings-api-key')?.value || '';
|
|
317
|
-
var resultEl = document.getElementById('settings-test-result');
|
|
318
|
-
|
|
319
|
-
if (!provider) {
|
|
320
|
-
if (resultEl) { resultEl.textContent = 'Select a provider first'; resultEl.className = 'ms-2 small text-danger'; }
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (resultEl) { resultEl.textContent = 'Testing...'; resultEl.className = 'ms-2 small text-muted'; }
|
|
325
|
-
|
|
326
|
-
try {
|
|
327
|
-
var resp = await fetch('/api/v3/provider/test', {
|
|
328
|
-
method: 'POST',
|
|
329
|
-
headers: {'Content-Type': 'application/json'},
|
|
330
|
-
body: JSON.stringify({provider: provider, model: model, api_key: apiKey})
|
|
331
|
-
});
|
|
332
|
-
var data = await resp.json();
|
|
333
|
-
if (data.success) {
|
|
334
|
-
if (resultEl) { resultEl.textContent = 'Connected! ' + (data.message || ''); resultEl.className = 'ms-2 small text-success fw-bold'; }
|
|
335
|
-
} else {
|
|
336
|
-
if (resultEl) { resultEl.textContent = 'Failed: ' + (data.error || 'Unknown'); resultEl.className = 'ms-2 small text-danger'; }
|
|
337
|
-
}
|
|
338
|
-
} catch (e) {
|
|
339
|
-
if (resultEl) { resultEl.textContent = 'Error: ' + e.message; resultEl.className = 'ms-2 small text-danger'; }
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async function saveAllSettings() {
|
|
344
|
-
var mode = document.querySelector('input[name="settings-mode-radio"]:checked')?.value || 'a';
|
|
345
|
-
var provider = document.getElementById('settings-provider')?.value || 'none';
|
|
346
|
-
if (mode === 'a') provider = 'none';
|
|
347
|
-
var model = document.getElementById('settings-model')?.value || '';
|
|
348
|
-
var apiKey = document.getElementById('settings-api-key')?.value || '';
|
|
349
|
-
|
|
350
|
-
var statusEl = document.getElementById('settings-save-status');
|
|
351
|
-
var saveBtn = document.getElementById('settings-save-all');
|
|
352
|
-
if (saveBtn) saveBtn.disabled = true;
|
|
353
|
-
if (statusEl) { statusEl.textContent = 'Saving...'; statusEl.style.display = 'inline'; statusEl.className = 'ms-2 text-muted'; }
|
|
354
|
-
|
|
355
|
-
try {
|
|
356
|
-
// Save mode
|
|
357
|
-
var modeResp = await fetch('/api/v3/mode/set', {
|
|
358
|
-
method: 'POST',
|
|
359
|
-
headers: {'Content-Type': 'application/json'},
|
|
360
|
-
body: JSON.stringify({mode: mode, provider: provider, model: model, api_key: apiKey})
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
if (modeResp.ok) {
|
|
364
|
-
if (statusEl) {
|
|
365
|
-
statusEl.textContent = 'Configuration saved! Mode: ' + mode.toUpperCase() +
|
|
366
|
-
(provider !== 'none' ? ' | Provider: ' + provider : '');
|
|
367
|
-
statusEl.className = 'ms-2 text-success fw-bold';
|
|
368
|
-
}
|
|
369
|
-
loadModeSettings();
|
|
370
|
-
} else {
|
|
371
|
-
if (statusEl) { statusEl.textContent = 'Save failed'; statusEl.className = 'ms-2 text-danger'; }
|
|
372
|
-
}
|
|
373
|
-
} catch (e) {
|
|
374
|
-
if (statusEl) { statusEl.textContent = 'Error: ' + e.message; statusEl.className = 'ms-2 text-danger'; }
|
|
375
|
-
}
|
|
376
|
-
if (saveBtn) saveBtn.disabled = false;
|
|
377
|
-
|
|
378
|
-
// Auto-hide status after 5 seconds
|
|
379
|
-
setTimeout(function() {
|
|
380
|
-
if (statusEl) statusEl.style.display = 'none';
|
|
381
|
-
}, 5000);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Bind events
|
|
385
|
-
document.getElementById('settings-provider')?.addEventListener('change', updateProviderUI);
|
|
386
|
-
document.getElementById('settings-save-all')?.addEventListener('click', saveAllSettings);
|
|
387
|
-
document.getElementById('settings-test-btn')?.addEventListener('click', testConnection);
|
|
388
|
-
|
|
389
|
-
// Mode radio buttons
|
|
390
|
-
document.querySelectorAll('input[name="settings-mode-radio"]').forEach(function(radio) {
|
|
391
|
-
radio.addEventListener('change', updateModeUI);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// Load settings when the settings tab is shown
|
|
395
|
-
document.getElementById('settings-tab')?.addEventListener('shown.bs.tab', function() {
|
|
396
|
-
loadAutoSettings();
|
|
397
|
-
loadModeSettings();
|
|
398
|
-
updateModeUI();
|
|
399
|
-
});
|