- Anthropic Messages API mit OAuth Token Auth - Chat Interface mit Message History - Tools: read_file, write_file, edit_file, list_files - Multi-Turn Tool Loop (max 25 Iterationen) - Steps-basiertes Response Format (Text + Tool interleaved) - Sandboxed Workspace für Dateioperationen
199 lines
8.2 KiB
HTML
199 lines
8.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Gott - Debug Console</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: monospace; background: #1a1a2e; color: #e0e0e0; display: flex; flex-direction: column; height: 100vh; }
|
|
header { padding: 12px 20px; border-bottom: 1px solid #333; display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
|
|
header h1 { color: #7c83ff; font-size: 1.2em; }
|
|
select { font-family: monospace; padding: 6px 10px; border: 1px solid #333; border-radius: 4px; background: #16213e; color: #e0e0e0; }
|
|
.meta-bar { padding: 4px 20px; background: #0a0a1a; border-bottom: 1px solid #222; font-size: 0.8em; color: #666; flex-shrink: 0; min-height: 24px; }
|
|
#chat { flex: 1; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 12px; }
|
|
.msg { max-width: 85%; padding: 10px 14px; border-radius: 8px; white-space: pre-wrap; word-break: break-word; line-height: 1.5; }
|
|
.msg.user { align-self: flex-end; background: #16213e; border: 1px solid #333; }
|
|
.msg.assistant { align-self: flex-start; background: #0f3460; border: 1px solid #1a4a7a; }
|
|
.msg.system-info { align-self: center; background: #1a1a2e; border: 1px solid #333; color: #888; font-size: 0.85em; text-align: center; max-width: 95%; }
|
|
.tool-calls { margin-top: 8px; padding-top: 8px; border-top: 1px solid #1a4a7a; }
|
|
.tool-call { margin: 4px 0; padding: 6px 8px; background: #0a1e3d; border-radius: 4px; font-size: 0.85em; }
|
|
.tool-name { color: #7c83ff; font-weight: bold; }
|
|
.tool-error { color: #ff6b6b; }
|
|
.tool-input { color: #888; margin-left: 8px; }
|
|
.tool-result { color: #6bff8a; font-size: 0.8em; margin-top: 2px; max-height: 100px; overflow-y: auto; }
|
|
.input-area { padding: 12px 20px; border-top: 1px solid #333; display: flex; gap: 8px; flex-shrink: 0; }
|
|
.input-area textarea { flex: 1; font-family: monospace; padding: 10px; border: 1px solid #333; border-radius: 4px; background: #0f3460; color: #e0e0e0; resize: none; font-size: 14px; min-height: 60px; max-height: 150px; }
|
|
.input-area button { padding: 10px 20px; background: #7c83ff; color: #fff; border: none; border-radius: 4px; font-family: monospace; font-weight: bold; cursor: pointer; align-self: flex-end; }
|
|
.input-area button:hover { background: #5a63e0; }
|
|
.input-area button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
.spinner { color: #7c83ff; }
|
|
.clear-btn { padding: 6px 10px; background: #333; color: #aaa; border: none; border-radius: 4px; cursor: pointer; font-family: monospace; font-size: 0.85em; }
|
|
.clear-btn:hover { background: #444; }
|
|
.system-toggle { display: flex; align-items: center; gap: 6px; margin-left: auto; }
|
|
.system-toggle label { font-size: 0.85em; color: #888; cursor: pointer; }
|
|
#systemPrompt { display: none; width: 100%; font-family: monospace; padding: 8px; border: 1px solid #333; border-radius: 4px; background: #0a0a1a; color: #e0e0e0; resize: vertical; font-size: 12px; min-height: 40px; margin: 0 20px; }
|
|
#systemPrompt.visible { display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header>
|
|
<h1>// gott</h1>
|
|
<select id="model">
|
|
<option value="sonnet">Sonnet 4.6</option>
|
|
<option value="opus">Opus 4.6</option>
|
|
</select>
|
|
<button class="clear-btn" onclick="clearChat()">Clear</button>
|
|
<div class="system-toggle">
|
|
<input type="checkbox" id="showSystem" onchange="toggleSystem()">
|
|
<label for="showSystem">System Prompt</label>
|
|
</div>
|
|
</header>
|
|
<textarea id="systemPrompt" placeholder="System Prompt (SOUL.md Inhalt etc.)"></textarea>
|
|
<div class="meta-bar" id="meta"></div>
|
|
|
|
<div id="chat"></div>
|
|
|
|
<div class="input-area">
|
|
<textarea id="input" placeholder="Nachricht eingeben... (Ctrl+Enter zum Senden)" rows="2"></textarea>
|
|
<button id="send" onclick="sendMessage()">Senden</button>
|
|
</div>
|
|
|
|
<script>
|
|
const messages = [];
|
|
|
|
function toggleSystem() {
|
|
document.getElementById('systemPrompt').classList.toggle('visible', document.getElementById('showSystem').checked);
|
|
}
|
|
|
|
function clearChat() {
|
|
messages.length = 0;
|
|
document.getElementById('chat').innerHTML = '';
|
|
document.getElementById('meta').textContent = '';
|
|
}
|
|
|
|
function addMessage(role, text, toolCalls) {
|
|
const chat = document.getElementById('chat');
|
|
const div = document.createElement('div');
|
|
div.className = 'msg ' + role;
|
|
div.textContent = text;
|
|
|
|
if (toolCalls && toolCalls.length > 0) {
|
|
const tc = document.createElement('div');
|
|
tc.className = 'tool-calls';
|
|
tc.innerHTML = '<div style="color:#888;font-size:0.8em;margin-bottom:4px;">Tool Calls (' + toolCalls.length + '):</div>';
|
|
for (const call of toolCalls) {
|
|
const d = document.createElement('div');
|
|
d.className = 'tool-call';
|
|
d.innerHTML = '<span class="tool-name">' + esc(call.name) + '</span>'
|
|
+ '<span class="tool-input">' + esc(call.input) + '</span>'
|
|
+ '<div class="tool-result ' + (call.isError ? 'tool-error' : '') + '">' + esc(call.result) + '</div>';
|
|
tc.appendChild(d);
|
|
}
|
|
div.appendChild(tc);
|
|
}
|
|
|
|
chat.appendChild(div);
|
|
chat.scrollTop = chat.scrollHeight;
|
|
}
|
|
|
|
function addSystemInfo(text) {
|
|
const chat = document.getElementById('chat');
|
|
const div = document.createElement('div');
|
|
div.className = 'msg system-info';
|
|
div.textContent = text;
|
|
chat.appendChild(div);
|
|
chat.scrollTop = chat.scrollHeight;
|
|
}
|
|
|
|
function renderSteps(steps) {
|
|
const chat = document.getElementById('chat');
|
|
// Gruppiere: text-steps und tool-steps zwischen texts zusammenfassen
|
|
let currentDiv = null;
|
|
let hasText = false;
|
|
|
|
for (const step of steps) {
|
|
if (step.type === 'text' && step.text) {
|
|
const div = document.createElement('div');
|
|
div.className = 'msg assistant';
|
|
div.textContent = step.text;
|
|
chat.appendChild(div);
|
|
currentDiv = div;
|
|
hasText = true;
|
|
} else if (step.type === 'tool') {
|
|
// Tool-Step als kompaktes Info-Element
|
|
const div = document.createElement('div');
|
|
div.className = 'msg system-info';
|
|
div.innerHTML = '<span class="tool-name">' + esc(step.toolName) + '</span>'
|
|
+ ' <span class="tool-input">' + esc(step.toolInput) + '</span>'
|
|
+ '<div class="tool-result ' + (step.isError ? 'tool-error' : '') + '">' + esc(step.toolResult) + '</div>';
|
|
chat.appendChild(div);
|
|
}
|
|
}
|
|
|
|
// Falls kein Text-Step am Ende war (nur tools), leeren assistant-msg anzeigen vermeiden
|
|
chat.scrollTop = chat.scrollHeight;
|
|
}
|
|
|
|
function esc(s) {
|
|
const d = document.createElement('div');
|
|
d.textContent = s;
|
|
return d.innerHTML;
|
|
}
|
|
|
|
async function sendMessage() {
|
|
const input = document.getElementById('input');
|
|
const btn = document.getElementById('send');
|
|
const meta = document.getElementById('meta');
|
|
const model = document.getElementById('model').value;
|
|
const systemPrompt = document.getElementById('systemPrompt').value.trim();
|
|
const text = input.value.trim();
|
|
if (!text) return;
|
|
|
|
messages.push({ role: 'user', content: text });
|
|
addMessage('user', text);
|
|
input.value = '';
|
|
|
|
btn.disabled = true;
|
|
meta.innerHTML = '<span class="spinner">⏳ Warte auf Antwort...</span>';
|
|
|
|
try {
|
|
const resp = await fetch('/api/chat', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ model, messages, systemPrompt })
|
|
});
|
|
const data = await resp.json();
|
|
if (!resp.ok) {
|
|
addSystemInfo('Fehler: ' + (data.detail || JSON.stringify(data)));
|
|
} else {
|
|
messages.push({ role: 'assistant', content: data.text });
|
|
renderSteps(data.steps || []);
|
|
|
|
const toolCount = (data.steps || []).filter(s => s.type === 'tool').length;
|
|
let metaText = `Model: ${data.model} | In: ${data.inputTokens} | Out: ${data.outputTokens}`;
|
|
if (data.iterations > 1) metaText += ` | Iterations: ${data.iterations}`;
|
|
if (toolCount > 0) metaText += ` | Tools: ${toolCount}`;
|
|
meta.textContent = metaText;
|
|
}
|
|
} catch (e) {
|
|
addSystemInfo('Netzwerk-Fehler: ' + e.message);
|
|
} finally {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
document.getElementById('input').addEventListener('keydown', e => {
|
|
if (e.ctrlKey && e.key === 'Enter') sendMessage();
|
|
});
|
|
|
|
// Auto-resize textarea
|
|
document.getElementById('input').addEventListener('input', function() {
|
|
this.style.height = 'auto';
|
|
this.style.height = Math.min(this.scrollHeight, 150) + 'px';
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|