Files
rfp_response_automation/files/templates/invalidate.html
2026-02-18 20:34:33 -03:00

203 lines
6.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% 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 %}