first commit

This commit is contained in:
2026-02-18 20:34:33 -03:00
parent 2f819da943
commit 60f0dcaac4
50 changed files with 8099 additions and 1471 deletions

View File

@@ -0,0 +1,203 @@
{% extends "base.html" %}
{% block content %}
<h1>🔐 RAG Knowledge Governance</h1>
<!-- ===================== -->
<!-- 🔎 INVALIDATE SEARCH -->
<!-- ===================== -->
<section style="margin-bottom: 40px;">
<h2>❌ Invalidate Knowledge</h2>
<form method="post" action="/admin/search">
<textarea name="statement" rows="4" style="width:100%;"
placeholder="Paste the invalid or outdated statement here">{{ statement }}</textarea>
<br><br>
<button type="submit">Search occurrences</button>
</form>
</section>
<hr>
<!-- ===================== -->
<!-- MANUAL KNOWLEDGE -->
<!-- ===================== -->
<section style="margin-bottom: 40px;">
<h2> Add Manual Knowledge</h2>
<form id="manualForm">
<textarea id="manualText" rows="8" style="width:100%;"
placeholder="Paste validated Oracle technical information here..."></textarea>
<br><br>
<input type="text" id="reason" style="width:100%;"
placeholder="Reason / justification (e.g. official Oracle clarification)"/>
<br><br>
<button type="button" onclick="submitManual()">Add Knowledge</button>
</form>
<pre id="manualResult" style="margin-top:15px;"></pre>
</section>
<hr>
<!-- ===================== -->
<!-- 📚 SEARCH RESULTS -->
<!-- ===================== -->
<section>
<h2>📚 Knowledge Matches</h2>
{% if results|length == 0 %}
<p><i>No matching knowledge found.</i></p>
{% endif %}
{% for r in results %}
<div style="border:1px solid #ccc; padding:15px; margin:15px 0; border-radius:6px;">
<strong>Chunk Hash:</strong>
<span data-hash-label>{{ r.chunk_hash or "—" }}</span><br>
<strong>Origin:</strong> {{ r.origin or "UNKNOWN" }}<br>
<strong>Created at:</strong> {{ r.created_at or "—" }}<br>
<strong>Status:</strong> {{ r.status }}<br>
<strong>Source:</strong> {{ r.source }}<br>
<div style="margin-top:10px;">
<strong>Content:</strong>
<pre style="
white-space: pre-wrap;
background:#f8f8f8;
padding:10px;
border-radius:4px;
border:1px solid #ddd;
max-height:400px;
overflow:auto;
">{{ r.text }}</pre>
<div style="margin-top:10px;">
<strong>Change to:</strong>
<textarea class="edit-box" data-hash="{{ r.chunk_hash }}"
style="
width:100%;
min-height:140px;
background:#f8f8f8;
padding:10px;
border-radius:4px;
border:1px solid #ddd;
font-family: monospace;
"
data-original="{{ r.text | e }}"
>{{ r.text }}</textarea>
</div>
</div>
{% if r.chunk_hash %}
<button
style="color:red;"
data-hash="{{ (r.chunk_hash or '') | string | e }}"
onclick="invalidateChunk(this)">
Invalidate
</button>
<button
style="color:blue; margin-left:10px;"
data-hash="{{ (r.chunk_hash or '') | string | e }}"
onclick="updateChunk(this)">
Change content
</button>
{% else %}
<p><i>Derived from Knowledge Graph (non-revocable)</i></p>
{% endif %}
</div>
{% endfor %}
</section>
<script>
async function submitManual() {
const text = document.getElementById("manualText").value;
const reason = document.getElementById("reason").value;
const res = await fetch("/admin/add-knowledge", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({ text, reason })
});
const data = await res.json();
document.getElementById("manualResult").textContent = JSON.stringify(data, null, 2);
}
async function invalidateChunk(btn) {
const chunkHash = btn.dataset.hash;
console.log("chunkHash:", chunkHash, "type:", typeof chunkHash);
const res = await fetch("/admin/revoke", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({
chunk_hash: chunkHash,
reason: "Invalidated via admin interface"
})
});
const data = await res.json().catch(() => ({}));
console.log("server:", res.status, data);
if (res.ok) btn.closest("div").remove();
}
async function updateChunk(btn) {
const card = btn.closest("div");
const chunkHash = btn.dataset.hash;
const textarea = card.querySelector(`.edit-box[data-hash="${chunkHash}"]`);
const newText = textarea.value;
if (!chunkHash) return;
if (!confirm("Update this chunk content?")) return;
btn.disabled = true;
btn.textContent = "Saving...";
const res = await fetch("/admin/update-chunk", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({
chunk_hash: chunkHash,
text: newText
})
});
const data = await res.json().catch(() => ({}));
btn.disabled = false;
if (res.ok) {
const newHash = data.chunk_hash;
const hashLabel = card.querySelector("[data-hash-label]");
if (hashLabel && newHash) {
hashLabel.textContent = newHash;
}
btn.dataset.hash = newHash;
textarea.style.borderColor = "green";
setTimeout(() => {
btn.textContent = "Change content";
textarea.style.borderColor = "#ddd";
}, 1200);
} else {
alert("Failed to update");
btn.textContent = "Change content";
}
}
</script>
{% endblock %}