mirror of
http://100.103.83.12:3003/fegger/pv-agent.git
synced 2026-09-17 16:06:23 +00:00
feat(agent): add test frontend and Docker stack
This commit is contained in:
+96
-64
@@ -1,69 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PV RAG Agent — Test-Chat</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
body { font-family: system-ui, sans-serif; max-width: 780px; margin: 2rem auto; padding: 0 1rem; }
|
||||
h1 { font-size: 1.2rem; }
|
||||
#log { display: flex; flex-direction: column; gap: 0.8rem; margin: 1.5rem 0; }
|
||||
.msg { border: 1px solid rgba(128,128,128,.35); border-radius: 8px; padding: .7rem .9rem; white-space: pre-wrap; }
|
||||
.q { background: rgba(128,128,128,.12); }
|
||||
.src { font-size: .78rem; margin-top: .4rem; display: flex; flex-wrap: wrap; gap: .3rem; }
|
||||
.chip { border-radius: 999px; padding: .1rem .5rem; background: rgba(128,128,128,.15); }
|
||||
.warn { border-color: #c08020; }
|
||||
form { display: flex; gap: .5rem; }
|
||||
input { flex: 1; padding: .5rem; border-radius: 6px; border: 1px solid rgba(128,128,128,.5); }
|
||||
button { padding: .5rem 1rem; border-radius: 6px; cursor: pointer; }
|
||||
.meta { font-size: .75rem; opacity: .7; }
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta</head> name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Testoberfläche für den PV RAG Agent">
|
||||
<title>PV Wissen — Test-Chat</title>
|
||||
<link rel="stylesheet" href="/assets/styles.css">
|
||||
<script src="/assets/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>PV RAG Agent — Wissensbasis Personalverrechnung</h1>
|
||||
<p class="meta">Antworten ausschließlich aus der kuratierten Wissensbasis, mit ID- und Stand-Beleg.</p>
|
||||
<div id="log"></div>
|
||||
<form id="f">
|
||||
<input id="q" placeholder="Frage zur österreichischen Personalverrechnung …" autocomplete="off" required>
|
||||
<button>Senden</button>
|
||||
</form>
|
||||
<script>
|
||||
const log = document.getElementById("log");
|
||||
function add(cls, html) {
|
||||
const d = document.createElement("div");
|
||||
d.className = "msg " + cls;
|
||||
d.innerHTML = html;
|
||||
log.appendChild(d);
|
||||
}
|
||||
document.getElementById("f").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const q = document.getElementById("q").value.trim();
|
||||
if (!q) return;
|
||||
add("q", q);
|
||||
document.getElementById("q").value = "";
|
||||
try {
|
||||
const r = await fetch("/ask", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ question: q }),
|
||||
});
|
||||
if (!r.ok) {
|
||||
add("warn", "Fehler " + r.status + ": " + (await r.text()));
|
||||
return;
|
||||
}
|
||||
const d = await r.json();
|
||||
const chips = (d.sources || []).map(s =>
|
||||
`<span class="chip">${s.id} · ${s.stand || ""}</span>`).join("");
|
||||
add(d.refused ? "warn" : "",
|
||||
`${d.answer.replace(/</g, "<")}` +
|
||||
(d.sources && d.sources.length ? `<div class="src">${chips}</div>` : "") +
|
||||
`<div class="meta">Modell: ${d.model} · ${d.latency_ms} ms · ` +
|
||||
`${d.verified ? "zitiergeprüft" : "UNVERIFIZIERT"}${d.refused ? " · verweigert" : ""}</div>`);
|
||||
} catch (err) {
|
||||
add("warn", "Netzwerkfehler: " + err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div class="shell">
|
||||
<header class="topbar">
|
||||
<a class="brand" href="/" aria-label="PV Wissen Startseite">
|
||||
<span class="brand-mark" aria-hidden="true">PV</span>
|
||||
<span>
|
||||
<strong>PV Wissen</strong>
|
||||
<small>Agent für österreichische Personalverrechnung</small>
|
||||
</span>
|
||||
</a>
|
||||
<div id="health" class="health health-loading" role="status" aria-live="polite">
|
||||
<span class="health-dot" aria-hidden="true"></span>
|
||||
<span>Verbindung wird geprüft</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero" aria-labelledby="page-title">
|
||||
<p class="eyebrow">Belegt. Nachvollziehbar. Lokal.</p>
|
||||
<h1 id="page-title">Was möchtest du zur Personalverrechnung wissen?</h1>
|
||||
<p>Antworten stammen ausschließlich aus der kuratierten Wissensbasis und führen ihre Quellen direkt an.</p>
|
||||
</section>
|
||||
|
||||
<section class="workspace">
|
||||
<aside class="settings" aria-labelledby="settings-title">
|
||||
<div class="settings-heading">
|
||||
<h2 id="settings-title">Verbindung</h2>
|
||||
<button id="settings-toggle" class="icon-button" type="button" aria-expanded="true" aria-controls="settings-body" title="Einstellungen ein- oder ausblenden">⚙</button>
|
||||
</div>
|
||||
<div id="settings-body">
|
||||
<label for="api-key">Service-Key</label>
|
||||
<div class="key-row">
|
||||
<input id="api-key" type="password" autocomplete="off" spellcheck="false" placeholder="Bearer-Key, falls aktiviert">
|
||||
<button id="key-visibility" class="secondary compact" type="button" aria-label="Service-Key anzeigen">Anzeigen</button>
|
||||
</div>
|
||||
<label class="check-row" for="remember-key">
|
||||
<input id="remember-key" type="checkbox">
|
||||
<span>Nur für diesen Tab merken</span>
|
||||
</label>
|
||||
|
||||
<label for="top-k">Kontextumfang</label>
|
||||
<select id="top-k">
|
||||
<option value="">Automatisch</option>
|
||||
<option value="6">6 Quellenblöcke</option>
|
||||
<option value="8">8 Quellenblöcke</option>
|
||||
<option value="12">12 Quellenblöcke</option>
|
||||
<option value="16">16 Quellenblöcke</option>
|
||||
</select>
|
||||
|
||||
<div class="privacy-note">
|
||||
<strong>Datenschutzgrenze</strong>
|
||||
<span>Keine Namen, Personalnummern oder Lohndaten eingeben. Diese Testversion verarbeitet nur Fachfragen.</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="chat" aria-label="Chat mit dem PV Agenten">
|
||||
<div id="empty-state" class="empty-state">
|
||||
<div class="empty-icon" aria-hidden="true">§</div>
|
||||
<h2>Mit einer Fachfrage starten</h2>
|
||||
<p>Zum Beispiel zu Reisekosten, Abgaben, Kollektivverträgen oder arbeitsrechtlichen Ansprüchen.</p>
|
||||
<div class="suggestions" aria-label="Beispielfragen">
|
||||
<button type="button" data-question="Ich will meinem Mitarbeiter 500 Euro zusätzlich auszahlen. Was ist die günstigste Lösung?">500 Euro zusätzlich auszahlen</button>
|
||||
<button type="button" data-question="Wie hoch ist der steuerfreie Tagesgeldsatz bei einer Inlandsdienstreise?">Steuerfreies Tagesgeld</button>
|
||||
<button type="button" data-question="Welche Voraussetzungen gelten für die Mitarbeiterprämie 2026?">Mitarbeiterprämie 2026</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="messages" class="messages" aria-live="polite" aria-label="Nachrichtenverlauf"></div>
|
||||
|
||||
<form id="ask-form" class="composer">
|
||||
<label class="sr-only" for="question">Frage</label>
|
||||
<textarea id="question" rows="3" maxlength="2000" required placeholder="Frage zur österreichischen Personalverrechnung …"></textarea>
|
||||
<div class="composer-footer">
|
||||
<span id="char-count" class="character-count">0 / 2000</span>
|
||||
<div class="composer-actions">
|
||||
<button id="cancel" class="secondary" type="button" hidden>Abbrechen</button>
|
||||
<button id="submit" class="primary" type="submit">
|
||||
<span>Frage senden</span>
|
||||
<span class="send-icon" aria-hidden="true">→</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="footnote">Der Agent kann Fehler machen. Fachliche Entscheidungen anhand der angeführten Quellen prüfen.</p>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user