from fastapi.responses import HTMLResponse
# ==========================================
# 6. GRAAFINEN VALVOMO-KCYTTCLIITTYMC (UI)
# ==========================================
def valvomo_ui():
return """
<!DOCTYPE html>
<html lang="fi">
<head>
<meta charset="UTF-8">
<title>Valvomo Dashboard</title>
<style>
body { font-family: system-ui, sans-serif; background: #1e1e1e;
color: #eee; margin: 0; padding: 20px; }
h1, h2 { border-bottom: 1px solid #333; padding-bottom: 10px; }
.container { display: flex; gap: 20px; }
.column { flex: 1; background: #252526; padding: 20px;
border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.3); }
.card { background: #333; padding: 15px; margin-bottom: 15px;
border-radius: 6px; border-left: 4px solid #007acc; }
.card.response { border-left-color: #28a745; }
pre { background: #1e1e1e; padding: 10px; border-radius: 4px;
overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;}
button { background: #007acc; color: white; border: none; padding:
8px 12px; cursor: pointer; border-radius: 4px; margin-right: 5px; margin-top:
10px;}
button.danger { background: #dc3545; }
button.success { background: #28a745; }
button:hover { opacity: 0.9; }
input[type="text"], textarea { width: 100%; background: #1e1e1e;
color: #eee; border: 1px solid #444; padding: 8px; margin-top: 5px;
box-sizing: border-box; }
</style>
</head>
<body>
<h1>Valvomo Proxy - Automaation Ohjauskeskus</h1>
<div class="card" style="border-left-color: #ffc107;">
<h3>Tila ja reititys</h3>
<button onclick="setMode('manual')">Tila: Manuaalinen</button>
<button onclick="setMode('all')">Tila: Salli kaikki</button>
<br><br>
<label>RegEx-hyvC$ksyntC$ (auto-approve):</label>
<input type="text" id="regexInput" placeholder="esim.
'click|liite'" onchange="setMode('regex', this.value)">
</div>
<div class="container">
<!-- PROMPTIT -->
<div class="column" id="prompts-col">
<h2>Saapuvat Kyselyt (Odottavat)</h2>
<div id="prompts-container">PC$ivitetC$C$n...</div>
</div>
<!-- VASTAUKSET -->
<div class="column" id="responses-col">
<h2>LLM Vastaukset (Odottavat julkaisua)</h2>
<div id="responses-container">PC$ivitetC$C$n...</div>
</div>
</div>
<script>
async function fetchData() {
// Hae promptit
let pRes = await fetch('/valvomo/prompts/pending');
let prompts = await pRes.json();
let pHTML = '';
for (let [id, data] of Object.entries(prompts)) {
let messages = data.request.messages.map(m => `[${m.role}]
${m.content}`).join('\
');
pHTML += `
<div class="card">
<small>ID: ${id}</small>
<pre>${messages}</pre>
<textarea id="manual-${id}" placeholder="Kirjoita
manuaalinen vastaus tC$hC$n..." rows="3"></textarea>
<button class="success" onclick="act('/valvomo/prom
pts/${id}/approve', 'POST')">HyvC$ksy & LC$hetC$ LLM</button>
<button onclick="manualAnswer('${id}')">Anna
Manuaalinen Vastaus</button>
<button class="danger" onclick="act('/valvomo/promp
ts/${id}/reject', 'POST')">HylkC$C$</button>
</div>
`;
}
document.getElementById('prompts-container').innerHTML = pHTML
|| '<p>Ei odottavia prompteja.</p>';
// Hae vastaukset
let rRes = await fetch('/valvomo/responses/pending');
let responses = await rRes.json();
let rHTML = '';
for (let [id, data] of Object.entries(responses)) {
rHTML += `
<div class="card response">
<small>ID: ${id} | LC$hde: ${data.source}</small>
<textarea id="edit-resp-${id}" rows="6">${data.cont
ent}</textarea>
<button class="success" onclick="editAndApproveResp
onse('${id}')">HyvC$ksy Vastaus (Vapauta botille)</button>
</div>
`;
}
document.getElementById('responses-container').innerHTML =
rHTML || '<p>Ei odottavia vastauksia.</p>';
}
async function act(url, method) {
await fetch(url, { method: method });
fetchData();
}
async function manualAnswer(id) {
let content = document.getElementById('manual-' + id).value;
if (!content) return alert("Kirjoita vastaus ensin!");
await fetch(`/valvomo/prompts/${id}/manual-answer?answer_conten
t=${encodeURIComponent(content)}`, { method: 'POST' });
fetchData();
}
async function editAndApproveResponse(id) {
let content = document.getElementById('edit-resp-' +
id).value;
await fetch(`/valvomo/responses/${id}?new_content=${encodeURICo
mponent(content)}`, { method: 'PUT' });
await fetch(`/valvomo/responses/${id}/approve`, { method:
'POST' });
fetchData();
}
async function setMode(mode, regex='') {
await fetch('/valvomo/mode', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mode: mode, regex_pattern: regex })
});
alert('Tila vaihdettu: ' + mode);
}
// PC$ivitC$ automaattisesti 2 sekunnin vC$lein
setInterval(fetchData, 2000);
fetchData();
</script>
</body>
</html>
"""