mirror of
https://github.com/ParisNeo/lollms_hub.git
synced 2026-05-04 03:01:01 -04:00
This commit updates several API routes, data store definitions, and related templates for the admin section. Changes include: - Refactoring in `admin.py`, `datastores.py`, `evaluations.py`, and `proxy.py`. - Updates to schema settings and static assets. - Modifications to various admin templates (`base.html`, `dashboard.html`, etc.). - Updates to agent management logic (`agentManager.ts`, `extensionState.ts`) and UI components.
511 lines
29 KiB
HTML
511 lines
29 KiB
HTML
{% extends "admin/base.html" %}
|
|
{% block title %}Neural Memory Cores{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="space-y-6">
|
|
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
|
<!-- Sidebar Controls -->
|
|
<div class="lg:col-span-1 space-y-6">
|
|
<div class="card-style bg-indigo-500/5 border-indigo-500/20">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-[10px] font-black text-indigo-400 uppercase tracking-widest">Target Matrix</h3>
|
|
<div class="flex items-center gap-2">
|
|
<label class="flex items-center gap-2 text-[9px] text-gray-500 uppercase font-bold cursor-pointer group">
|
|
<input type="checkbox" id="auto-refresh-memory" class="h-3 w-3 rounded text-indigo-600 bg-black border-white/20">
|
|
<span class="group-hover:text-indigo-300">Live</span>
|
|
</label>
|
|
<button onclick="loadMemoryData()" class="p-1.5 bg-white/5 hover:bg-white/10 rounded-lg text-indigo-400 transition-all" title="Manual Refresh">
|
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-4">
|
|
<div class="mb-4">
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1 px-1">1. Select Subject (Scope)</label>
|
|
<select id="user-selector" onchange="loadMemoryData()" class="w-full p-2.5 rounded-lg bg-indigo-600/10 border border-indigo-500/30 text-indigo-400 font-black uppercase text-[10px] tracking-widest outline-none transition-all focus:border-indigo-400">
|
|
<optgroup label="System Scopes">
|
|
<option value="system">System (Immutable ROM) ({{ counts.get('system', 0) }})</option>
|
|
<option value="shared_knowledge">Global Collective (Shared) ({{ counts.get('shared_knowledge', 0) }})</option>
|
|
</optgroup>
|
|
<optgroup label="User Identities">
|
|
{% for u in active_users %}
|
|
{% if u.id != 'system' %}
|
|
{# Ensure value is the string representation of the ID #}
|
|
<option value="{{ u.id }}">{{ u.display | upper }} ({{ u.count }})</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[9px] text-gray-500 uppercase font-bold mb-1 px-1">2. Target Memory Core</label>
|
|
<div class="flex gap-2">
|
|
<select id="sys-select" class="flex-grow bg-black/40 border border-white/10 p-2 rounded text-xs outline-none focus:border-indigo-500" onchange="loadMemoryData()">
|
|
{% for s in systems %}<option value="{{ s.name }}">{{ s.name }}</option>{% endfor %}
|
|
</select>
|
|
<button onclick="addMemorySystemModal()" class="px-2 bg-white/5 border border-white/10 rounded text-indigo-400 hover:bg-white/10 transition-colors">+</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-style bg-black/20 border-white/5 space-y-3">
|
|
<h3 class="text-[10px] font-black text-gray-600 uppercase tracking-widest">Core Operations</h3>
|
|
<button onclick="triggerDream()" id="btn-dream" class="w-full py-3 bg-indigo-600/10 border border-indigo-500/30 text-indigo-300 rounded-xl text-[10px] font-black uppercase hover:bg-indigo-600/20 transition-all flex items-center justify-center gap-2">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path></svg>
|
|
Force Dream Cycle
|
|
</button>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<button onclick="exportMemory()" class="py-3 bg-white/5 border border-white/10 rounded-xl text-[9px] font-black uppercase text-gray-400 hover:text-white">
|
|
<svg class="w-3 h-3 mx-auto mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
|
Teleport Out
|
|
</button>
|
|
<button onclick="document.getElementById('import-file').click()" class="py-3 bg-white/5 border border-white/10 rounded-xl text-[9px] font-black uppercase text-gray-400 hover:text-white">
|
|
<svg class="w-3 h-3 mx-auto mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
|
|
Teleport In
|
|
</button>
|
|
<input type="file" id="import-file" class="hidden" onchange="importMemory(event)">
|
|
</div>
|
|
<button onclick="wipeCore()" class="w-full py-2 bg-red-900/20 text-red-400 border border-red-500/20 rounded-lg text-[9px] font-black uppercase hover:bg-red-900/40">Wipe Core (Neural Purge)</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Viewer Area -->
|
|
<div class="lg:col-span-3 space-y-6">
|
|
<!-- Affective Matrix -->
|
|
<div id="affective-matrix-container" class="card-style bg-black/40 border-white/5 mb-8">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h3 class="text-[10px] font-black text-indigo-400 uppercase tracking-widest">Affective Relationship Matrix</h3>
|
|
<label class="flex items-center gap-3 cursor-pointer group">
|
|
<input type="checkbox" id="toggle-affective-cb" onchange="toggleAffectiveFeature()" class="h-4 w-4 rounded text-indigo-600 bg-black border-white/20">
|
|
<span class="text-[10px] font-black text-gray-500 uppercase tracking-widest group-hover:text-white transition-colors">Activate Logic</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div id="affective-content-area" class="opacity-30 pointer-events-none transition-all duration-300">
|
|
<div class="px-10 space-y-8">
|
|
<div class="flex justify-between text-[8px] font-black uppercase tracking-tighter">
|
|
<span class="text-red-500">Hostility</span>
|
|
<span class="text-gray-600">Neutral (Asimov Base)</span>
|
|
<span class="text-emerald-500">Worship/Respect</span>
|
|
</div>
|
|
<div class="relative h-1 bg-white/10 rounded-full">
|
|
<div id="aff-dot" class="absolute -top-2 w-5 h-5 bg-red-500 rounded-full border-4 border-black shadow-lg shadow-red-500/20 transition-all duration-500" style="left: 50%;"></div>
|
|
</div>
|
|
<div class="bg-black/60 p-6 rounded-xl border border-white/5 text-center italic text-gray-400 text-sm" id="aff-content">Neutral</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Memory List -->
|
|
<div class="card-style bg-black/40 border-white/5 flex-grow flex flex-col overflow-hidden">
|
|
<div class="flex bg-black/40 p-1 rounded-xl border border-white/10 w-fit mb-6">
|
|
<button onclick="switchTab('engrams')" id="tab-btn-engrams" class="px-10 py-2 rounded-lg text-[10px] font-black uppercase transition-all bg-indigo-600 text-white">🧶 Active</button>
|
|
<button onclick="switchTab('deep')" id="tab-btn-deep" class="px-10 py-2 rounded-lg text-[10px] font-black uppercase transition-all text-gray-500 hover:text-white">🕳️ Deep Memory</button>
|
|
<button onclick="switchTab('dreams')" id="tab-btn-dreams" class="px-10 py-2 rounded-lg text-[10px] font-black uppercase transition-all text-gray-500 hover:text-white">🚀 Dreams</button>
|
|
</div>
|
|
|
|
<div id="view-engrams" class="flex flex-col flex-grow overflow-hidden">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<p class="text-xs text-gray-600">Tier 1: High-importance facts currently in working context.</p>
|
|
<button onclick="openAddEngram()" class="bg-white/5 hover:bg-white/10 border border-white/10 px-4 py-1.5 rounded-lg text-[10px] font-black uppercase text-gray-300 transition-all">+ New Engram</button>
|
|
</div>
|
|
|
|
<div class="overflow-y-auto custom-scrollbar flex-grow">
|
|
<table class="w-full text-left">
|
|
<thead>
|
|
<tr class="text-[9px] font-black text-gray-600 uppercase border-b border-white/5">
|
|
<th class="p-3">Category</th>
|
|
<th class="p-3">Fact</th>
|
|
<th class="p-3">Strength</th>
|
|
<th class="p-3 text-right">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="engrams-tbody" class="divide-y divide-white/5"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Deep Memory Tab -->
|
|
<div id="view-deep" class="hidden flex flex-col flex-grow overflow-hidden">
|
|
<div class="mb-4">
|
|
<p class="text-xs text-gray-600">Tier 2: Archived handles. The AI knows these exist but cannot see details until recalled via <code class="text-indigo-400"><memory_search/></code>.</p>
|
|
</div>
|
|
<div class="overflow-y-auto custom-scrollbar flex-grow">
|
|
<table class="w-full text-left">
|
|
<thead>
|
|
<tr class="text-[9px] font-black text-gray-600 uppercase border-b border-white/5">
|
|
<th class="p-3">Category</th>
|
|
<th class="p-3">Archived Handle</th>
|
|
<th class="p-3">Strength</th>
|
|
<th class="p-3 text-right">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="deep-tbody" class="divide-y divide-white/5"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dreams Tab -->
|
|
<div id="view-dreams" class="hidden flex flex-col flex-grow overflow-hidden">
|
|
<div class="mb-4">
|
|
<p class="text-xs text-gray-600">Neural Reorganization Logs: Automated maintenance performed during idle time.</p>
|
|
</div>
|
|
<div id="dream-list" class="space-y-3 overflow-y-auto custom-scrollbar flex-grow pr-2">
|
|
<!-- Populated by JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentView = 'engrams';
|
|
|
|
function addMemorySystemModal() {
|
|
const body = `
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Core Name</label>
|
|
<input type="text" id="new-sys-name" placeholder="e.g. specialized_agent_core" class="w-full bg-black/40 border border-white/10 p-2 rounded-lg text-sm">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Description</label>
|
|
<input type="text" id="new-sys-desc" placeholder="What is this core used for?" class="w-full bg-black/40 border border-white/10 p-2 rounded-lg text-sm">
|
|
</div>
|
|
<label class="flex items-center gap-2 cursor-pointer mt-4">
|
|
<input type="checkbox" id="new-sys-aff" class="h-4 w-4 rounded text-indigo-600 bg-black border-white/20">
|
|
<span class="text-xs font-bold text-gray-400">Enable Affective Matrix</span>
|
|
</label>
|
|
<div class="flex justify-end pt-4 border-t border-white/5">
|
|
<button onclick="submitNewSystem()" class="bg-indigo-600 hover:bg-indigo-500 px-8 py-2 rounded-xl text-xs font-black uppercase text-white shadow-lg transition-all">Create Core</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
window.showModal("Construct Memory Core", body);
|
|
}
|
|
|
|
async function submitNewSystem() {
|
|
const fd = new FormData();
|
|
fd.append('name', document.getElementById('new-sys-name').value);
|
|
fd.append('desc', document.getElementById('new-sys-desc').value);
|
|
fd.append('use_aff', document.getElementById('new-sys-aff').checked);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
await fetch('/admin/api/memory/system/add', { method: 'POST', body: fd });
|
|
window.location.reload();
|
|
}
|
|
|
|
async function deleteMemorySystem() {
|
|
const system = document.getElementById('sys-select').value;
|
|
if (system === 'lollms' || system === 'default') {
|
|
alert(`Cannot delete the foundational '${system}' core.`);
|
|
return;
|
|
}
|
|
if(!confirm(`Are you sure you want to completely destroy the '${system}' memory core and ALL its engrams? This is irreversible.`)) return;
|
|
|
|
try {
|
|
const resp = await fetch(`/admin/api/memory/system/${system}`, {
|
|
method: 'DELETE',
|
|
headers: { 'X-CSRF-Token': '{{ csrf_token }}' }
|
|
});
|
|
if (resp.ok) {
|
|
window.location.reload();
|
|
} else {
|
|
const data = await resp.json();
|
|
alert(data.error || "Failed to delete core.");
|
|
}
|
|
} catch(e) {
|
|
alert("Error: " + e.message);
|
|
}
|
|
}
|
|
|
|
function switchTab(tab) {
|
|
['engrams', 'deep', 'dreams'].forEach(t => {
|
|
const el = document.getElementById(`view-${t}`);
|
|
const btn = document.getElementById(`tab-btn-${t}`);
|
|
if (el) el.classList.toggle('hidden', tab !== t);
|
|
if (btn) {
|
|
btn.classList.toggle('bg-indigo-600', tab === t);
|
|
btn.classList.toggle('text-white', tab === t);
|
|
btn.classList.toggle('text-gray-500', tab !== t);
|
|
}
|
|
});
|
|
}
|
|
|
|
async function toggleAffectiveFeature() {
|
|
const systemSelect = document.getElementById('sys-select');
|
|
if (!systemSelect) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('system_name', systemSelect.value);
|
|
const resp = await fetch('/admin/api/memory/system/toggle_affective', { method: 'POST', body: formData });
|
|
const data = await resp.json();
|
|
updateAffectiveUI(data.state);
|
|
}
|
|
|
|
function updateAffectiveUI(active) {
|
|
const container = document.getElementById('affective-content-area');
|
|
const cb = document.getElementById('toggle-affective-cb');
|
|
if (cb) cb.checked = active;
|
|
if (container) {
|
|
if (active) {
|
|
container.classList.remove('opacity-30', 'pointer-events-none');
|
|
} else {
|
|
container.classList.add('opacity-30', 'pointer-events-none');
|
|
}
|
|
}
|
|
}
|
|
|
|
async function updateImportance(id, val) {
|
|
const formData = new FormData();
|
|
formData.append('m_id', id);
|
|
formData.append('importance', val);
|
|
formData.append('csrf_token', '{{ csrf_token }}');
|
|
await fetch('/admin/api/memory/importance', { method: 'POST', body: formData });
|
|
loadMemoryData();
|
|
}
|
|
|
|
async function deleteEntry(id) {
|
|
if (!confirm("Purge this engram?")) return;
|
|
await fetch(`/admin/api/memory/entry/${id}`, {
|
|
method: 'DELETE',
|
|
headers: { 'X-CSRF-Token': '{{ csrf_token }}' }
|
|
});
|
|
loadMemoryData();
|
|
}
|
|
|
|
async function loadMemoryData() {
|
|
const systemSelect = document.getElementById('sys-select');
|
|
const userSelect = document.getElementById('user-selector');
|
|
if (!systemSelect || !userSelect) return;
|
|
|
|
const system = systemSelect.value;
|
|
const user = userSelect.value;
|
|
if (!system || !user) return;
|
|
|
|
try {
|
|
const resp = await fetch(`/admin/api/memory/data?system=${encodeURIComponent(system)}&user=${encodeURIComponent(user)}`);
|
|
const data = await resp.json();
|
|
|
|
// 1. Update Affective System status and dot
|
|
updateAffectiveUI(data.system_use_affective);
|
|
|
|
const affDot = document.getElementById('aff-dot');
|
|
const affContent = document.getElementById('aff-content');
|
|
if (affContent) affContent.innerText = data.affective.content || "Neutral";
|
|
if (affDot) {
|
|
const text = (data.affective.content || "").toLowerCase();
|
|
let pos = "50%";
|
|
if (text.includes("hostile") || text.includes("enemy") || text.includes("threat") || text.includes("angry")) pos = "10%";
|
|
else if (text.includes("friendly") || text.includes("love") || text.includes("respect") || text.includes("worship")) pos = "90%";
|
|
affDot.style.left = pos;
|
|
}
|
|
|
|
// 2. Render Engrams (Active & Deep)
|
|
const renderRow = m => {
|
|
// Escape strings for JS function call
|
|
const safeTitle = (m.title || "").replace(/'/g, "\\'");
|
|
const safeContent = (m.content || "").replace(/'/g, "\\'");
|
|
const safeCat = (m.cat || "").replace(/'/g, "\\'");
|
|
|
|
return `
|
|
<tr class="group/row">
|
|
<td class="p-3 text-[9px] font-bold text-indigo-500 uppercase">
|
|
${m.is_immutable ? '<span class="text-amber-500">[ROM]</span> ' : (m.user_id === 'shared_knowledge' ? '<span class="text-emerald-400">[SHARED]</span> ' : '')}${m.cat}
|
|
</td>
|
|
<td class="p-3 text-sm">
|
|
<div class="font-black text-white text-[11px] mb-0.5">${m.title}</div>
|
|
<div class="text-xs text-gray-400 leading-relaxed">${m.content}</div>
|
|
</td>
|
|
<td class="p-3">
|
|
<div class="flex items-center gap-2">
|
|
<input type="number" onchange="updateImportance(${m.id}, this.value)" value="${m.imp}"
|
|
class="w-10 bg-black/40 border border-white/5 rounded text-[10px] text-center text-indigo-400 px-1">
|
|
<div class="w-12 bg-white/5 h-1 rounded-full overflow-hidden flex-shrink-0">
|
|
<div class="bg-indigo-500 h-full" style="width: ${m.imp}%"></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="p-3 text-right whitespace-nowrap">
|
|
<div class="flex justify-end gap-3 opacity-0 group-hover/row:opacity-100 transition-opacity">
|
|
<button onclick="openEditEngram(${m.id}, '${safeCat}', '${safeTitle}', '${safeContent}')" class="text-[9px] font-black text-indigo-400 hover:text-white uppercase tracking-widest">Edit</button>
|
|
${m.imp >= 25 ? `<button onclick="updateImportance(${m.id}, 10)" class="text-[9px] font-black text-gray-500 hover:text-indigo-400 uppercase tracking-widest">Archive</button>` : ''}
|
|
<button onclick="deleteEntry(${m.id})" class="text-red-500 hover:text-red-400 font-black text-sm">×</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
};
|
|
|
|
const engramsBody = document.getElementById('engrams-tbody');
|
|
const deepBody = document.getElementById('deep-tbody');
|
|
if (engramsBody) engramsBody.innerHTML = data.memories.filter(m => m.imp >= 25).map(renderRow).join('');
|
|
if (deepBody) deepBody.innerHTML = data.memories.filter(m => m.imp < 25).map(renderRow).join('');
|
|
|
|
// 3. Render Dreams
|
|
const dreamList = document.getElementById('dream-list');
|
|
if (dreamList) {
|
|
if (data.dreams && data.dreams.length > 0) {
|
|
dreamList.innerHTML = data.dreams.map(d => `
|
|
<div class="p-3 bg-black/40 border border-white/5 rounded-lg">
|
|
<div class="text-[9px] font-black text-indigo-500 uppercase mb-1">${d.ts}</div>
|
|
<div class="text-xs text-gray-400">${d.summary}</div>
|
|
</div>
|
|
`).join('');
|
|
} else {
|
|
dreamList.innerHTML = '<div class="p-10 text-center text-gray-600 italic">No reorganization logs found. core has not dreamt yet.</div>';
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error("Failed to load memory data:", e);
|
|
}
|
|
}
|
|
|
|
// --- AUTO-REFRESH LOGIC ---
|
|
let refreshInterval = null;
|
|
const setupAutoRefresh = () => {
|
|
const cb = document.getElementById('auto-refresh-memory');
|
|
cb.addEventListener('change', (e) => {
|
|
if (e.target.checked) {
|
|
console.log("Memory Auto-refresh active (3s)");
|
|
refreshInterval = setInterval(loadMemoryData, 3000);
|
|
} else {
|
|
clearInterval(refreshInterval);
|
|
refreshInterval = null;
|
|
}
|
|
});
|
|
};
|
|
document.addEventListener('DOMContentLoaded', setupAutoRefresh);
|
|
|
|
async function triggerDream() {
|
|
const btn = document.getElementById('btn-dream');
|
|
const sys = document.getElementById('sys-select').value;
|
|
const user = document.getElementById('user-selector').value;
|
|
btn.disabled = true;
|
|
btn.innerText = "DREAMING...";
|
|
|
|
const fd = new FormData();
|
|
fd.append('system', sys);
|
|
fd.append('user', user);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
|
|
try {
|
|
await fetch('/admin/api/memory/trigger_dream', { method: 'POST', body: fd });
|
|
setTimeout(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = 'Force Dream Cycle';
|
|
loadMemoryData();
|
|
}, 1500);
|
|
} catch (e) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = 'Force Dream Cycle';
|
|
}
|
|
}
|
|
|
|
function exportMemory() {
|
|
const sys = document.getElementById('sys-select').value;
|
|
const user = document.getElementById('user-selector').value;
|
|
window.location.href = `/admin/api/memory/export?system=${sys}&user=${user}`;
|
|
}
|
|
|
|
async function importMemory(e) {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
const fd = new FormData();
|
|
fd.append('system', document.getElementById('sys-select').value);
|
|
fd.append('user', document.getElementById('user-selector').value);
|
|
fd.append('file', file);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
await fetch('/admin/api/memory/import', { method: 'POST', body: fd });
|
|
loadMemoryData();
|
|
}
|
|
|
|
function openEditEngram(id, cat, title, content) {
|
|
const body = `
|
|
<div class="space-y-4 text-left">
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Category</label>
|
|
<input type="text" id="edit-m-cat" value="${cat}" class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Engram Title</label>
|
|
<input type="text" id="edit-m-title" value="${title}" class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Content</label>
|
|
<textarea id="edit-m-content" class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm h-32 leading-relaxed">${content}</textarea>
|
|
</div>
|
|
<div class="flex justify-end pt-4 border-t border-white/5">
|
|
<button onclick="submitEditEngram(${id})" class="bg-indigo-600 hover:bg-indigo-500 px-10 py-2 rounded-xl text-xs font-black uppercase text-white shadow-lg transition-all">Apply Sculpting</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
window.showModal("Sculpt Existing Engram", body);
|
|
}
|
|
|
|
async function submitEditEngram(id) {
|
|
const fd = new FormData();
|
|
fd.append('m_id', id);
|
|
fd.append('category', document.getElementById('edit-m-cat').value);
|
|
fd.append('title', document.getElementById('edit-m-title').value);
|
|
fd.append('content', document.getElementById('edit-m-content').value);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
|
|
const resp = await fetch('/admin/api/memory/entry/update', { method: 'POST', body: fd });
|
|
if (resp.ok) {
|
|
document.getElementById('modal-close-btn').click();
|
|
loadMemoryData();
|
|
window.showToast("Engram successfully sculpted.", "success");
|
|
}
|
|
}
|
|
|
|
function openAddEngram() {
|
|
const body = `
|
|
<div class="space-y-4 text-left">
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Category</label>
|
|
<input type="text" id="m-cat" placeholder="User Info, Preference, Fact..." class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Engram Title</label>
|
|
<input type="text" id="m-title" placeholder="e.g. favorite_color" class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] font-black text-gray-500 uppercase mb-1">Content</label>
|
|
<textarea id="m-content" placeholder="Details to store..." class="w-full bg-black/40 p-2 rounded-lg border border-white/10 text-sm h-24"></textarea>
|
|
</div>
|
|
<div class="flex justify-end pt-4">
|
|
<button onclick="submitMemory()" class="bg-indigo-600 hover:bg-indigo-500 px-10 py-2 rounded-xl text-xs font-black uppercase text-white shadow-lg">Commit to Core</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
window.showModal("New Neural Engram", body);
|
|
}
|
|
|
|
async function submitMemory() {
|
|
const fd = new FormData();
|
|
fd.append('system', document.getElementById('sys-select').value);
|
|
fd.append('user', document.getElementById('user-selector').value);
|
|
fd.append('category', document.getElementById('m-cat').value);
|
|
fd.append('title', document.getElementById('m-title').value);
|
|
fd.append('content', document.getElementById('m-content').value);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
await fetch('/admin/api/memory/entry', { method: 'POST', body: fd });
|
|
document.getElementById('modal-close-btn').click();
|
|
loadMemoryData();
|
|
}
|
|
|
|
async function wipeCore() {
|
|
if (!confirm("CRITICAL: This will permanently delete all memories and dream logs for this user in this core. Proceed?")) return;
|
|
const fd = new FormData();
|
|
fd.append('system', document.getElementById('sys-select').value);
|
|
fd.append('user', document.getElementById('user-selector').value);
|
|
fd.append('csrf_token', '{{ csrf_token }}');
|
|
await fetch('/admin/api/memory/wipe', { method: 'POST', body: fd });
|
|
loadMemoryData();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadMemoryData);
|
|
</script>
|
|
{% endblock %} |