remove tests
This commit is contained in:
@@ -1,335 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>AI Agent</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
<!-- Markdown + Sanitizer -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg: #0f172a;
|
|
||||||
--panel: #020617;
|
|
||||||
--border: #334155;
|
|
||||||
--text: #e5e7eb;
|
|
||||||
--muted: #94a3b8;
|
|
||||||
--accent: #2563eb;
|
|
||||||
--user: #1e40af;
|
|
||||||
--assistant: #020617;
|
|
||||||
--danger: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: system-ui, sans-serif;
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
margin: 0;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: calc(100vh - 4rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header .spacer {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 1rem;
|
|
||||||
background: var(--panel);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin-bottom: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.user {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.user .bubble {
|
|
||||||
background: var(--user);
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.assistant .bubble {
|
|
||||||
background: var(--assistant);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble {
|
|
||||||
max-width: 80%;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble.loader {
|
|
||||||
font-style: italic;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Markdown styling */
|
|
||||||
.bubble p { margin: 0.4rem 0; }
|
|
||||||
.bubble ul { padding-left: 1.2rem; }
|
|
||||||
.bubble code {
|
|
||||||
background: rgba(148, 163, 184, 0.15);
|
|
||||||
padding: 0.15rem 0.35rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, monospace;
|
|
||||||
}
|
|
||||||
.bubble pre {
|
|
||||||
background: #020617;
|
|
||||||
border: 1px solid #334155;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 0.75rem;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-area {
|
|
||||||
margin-top: 1rem;
|
|
||||||
display: flex;
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
flex: 1;
|
|
||||||
resize: none;
|
|
||||||
min-height: 60px;
|
|
||||||
max-height: 120px;
|
|
||||||
padding: 0.75rem;
|
|
||||||
background: var(--panel);
|
|
||||||
color: var(--text);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 0.6rem 1rem;
|
|
||||||
background: var(--accent);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.abort { background: var(--danger); }
|
|
||||||
|
|
||||||
button.secondary {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
<h1>mitho AI Agent</h1>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
<button id="clear" class="secondary">Diesen Chat löschen</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="chat" class="chat"></div>
|
|
||||||
|
|
||||||
<div class="input-area">
|
|
||||||
<textarea id="prompt" placeholder="Ask something…">gebe eine lister alle leistungen von mitho aus</textarea>
|
|
||||||
<button id="send">Send</button>
|
|
||||||
<button id="abort" class="abort" disabled>Abbrechen</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
const chatEl = document.getElementById('chat');
|
|
||||||
const promptEl = document.getElementById('prompt');
|
|
||||||
const sendBtn = document.getElementById('send');
|
|
||||||
const abortBtn = document.getElementById('abort');
|
|
||||||
const clearBtn = document.getElementById('clear');
|
|
||||||
|
|
||||||
let abort = false;
|
|
||||||
|
|
||||||
marked.setOptions({ breaks: true });
|
|
||||||
|
|
||||||
function renderMarkdown(text) {
|
|
||||||
console.log(text,marked.parse(text))
|
|
||||||
return DOMPurify.sanitize(marked.parse(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderStreaming(text) {
|
|
||||||
return text
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>');
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeMarkdown(text) {
|
|
||||||
return text
|
|
||||||
|
|
||||||
// HTML Entities entschärfen
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
|
|
||||||
// Füge Zeilenumbrüche vor und nach Markdown-Überschriften (###, #### etc.)
|
|
||||||
.replace(/\s*(#{1,6}.*?)\s*(?=#+|$)/g, '\n\n$1\n\n')
|
|
||||||
|
|
||||||
// Füge Zeilenumbrüche vor und nach Listenpunkten
|
|
||||||
.replace(/\s*-\s+/g, '\n- ')
|
|
||||||
|
|
||||||
// Füge Zeilenumbrüche vor horizontalen Linien (---)
|
|
||||||
.replace(/\s*---+\s*/g, '\n\n---\n\n')
|
|
||||||
|
|
||||||
// Entferne mehrfach aufeinanderfolgende Leerzeilen
|
|
||||||
.replace(/\n{3,}/g, '\n\n')
|
|
||||||
|
|
||||||
// Entferne führende und nachfolgende Leerzeichen
|
|
||||||
.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function addMessage(role, html = '', extra = '') {
|
|
||||||
const msg = document.createElement('div');
|
|
||||||
msg.className = 'message ' + role;
|
|
||||||
|
|
||||||
const bubble = document.createElement('div');
|
|
||||||
bubble.className = 'bubble ' + extra;
|
|
||||||
bubble.innerHTML = html;
|
|
||||||
|
|
||||||
msg.appendChild(bubble);
|
|
||||||
chatEl.appendChild(msg);
|
|
||||||
chatEl.scrollTop = chatEl.scrollHeight;
|
|
||||||
|
|
||||||
return bubble;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addLoader() {
|
|
||||||
return addMessage('assistant', 'AI is typing…', 'loader');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadHistory() {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/history');
|
|
||||||
if (!res.ok) return;
|
|
||||||
const messages = await res.json();
|
|
||||||
messages.forEach(m =>
|
|
||||||
addMessage(m.role, renderMarkdown(m.text))
|
|
||||||
);
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', loadHistory);
|
|
||||||
|
|
||||||
sendBtn.addEventListener('click', async () => {
|
|
||||||
const prompt = promptEl.value.trim();
|
|
||||||
if (!prompt) return;
|
|
||||||
|
|
||||||
addMessage('user', renderMarkdown(prompt));
|
|
||||||
promptEl.value = '';
|
|
||||||
|
|
||||||
const bubble = addLoader();
|
|
||||||
let raw = '';
|
|
||||||
let first = true;
|
|
||||||
|
|
||||||
abort = false;
|
|
||||||
sendBtn.disabled = true;
|
|
||||||
abortBtn.disabled = false;
|
|
||||||
clearBtn.disabled = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('/ask-sse', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ prompt })
|
|
||||||
});
|
|
||||||
|
|
||||||
const reader = res.body.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
|
|
||||||
while (!abort) {
|
|
||||||
const { value, done } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
decoder.decode(value, { stream: true })
|
|
||||||
.split('\n')
|
|
||||||
.forEach(line => {
|
|
||||||
if (!line.startsWith('data: ')) return;
|
|
||||||
|
|
||||||
const text = line.slice(6);
|
|
||||||
|
|
||||||
if (text === '[DONE]') {
|
|
||||||
const normalized = normalizeMarkdown(raw);
|
|
||||||
bubble.innerHTML = renderMarkdown(normalized);
|
|
||||||
abort = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first) {
|
|
||||||
bubble.classList.remove('loader');
|
|
||||||
bubble.innerHTML = '';
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
raw += text;
|
|
||||||
bubble.innerHTML = renderStreaming(raw);
|
|
||||||
chatEl.scrollTop = chatEl.scrollHeight;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
bubble.innerHTML += '<br><em>Error occurred.</em>';
|
|
||||||
} finally {
|
|
||||||
sendBtn.disabled = false;
|
|
||||||
abortBtn.disabled = true;
|
|
||||||
clearBtn.disabled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
abortBtn.addEventListener('click', () => {
|
|
||||||
abort = true;
|
|
||||||
addMessage('assistant', '<em>[aborted]</em>');
|
|
||||||
});
|
|
||||||
|
|
||||||
clearBtn.addEventListener('click', async () => {
|
|
||||||
await fetch('/history/delete', { method: 'POST' });
|
|
||||||
chatEl.innerHTML = '';
|
|
||||||
addMessage('assistant', '<em>History cleared.</em>');
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user