feat: Dynamische KI-Modelle, verbessertes Memory-System und Chat-Überarbeitung
🎯 KI-Modellverwaltung - Dynamisches Laden verfügbarer Modelle via opencode models - 29 Modelle verfügbar (opencode, anthropic, ollama) - Gruppierung nach Anbieter in UI - Cache-Mechanismus (1h TTL) für Performance - API-Endpoint /api/models für Modellabfrage 🧠 Memory-System komplett überarbeitet - JSON-basierte strukturierte Erinnerungen statt Markdown-Chaos - Separate Memory-Typen: tasks.json, notes.json, research.json - Automatische Memory-Zusammenfassung im Systemprompt - Limitierung auf letzte 100 Einträge pro Typ - Vollständige Task-Ergebnisse statt abgeschnittener Texte 📁 Agenten-Ordnerstruktur - work/ Verzeichnis für Agent-Dateien - memory/ Verzeichnis für strukturierte Erinnerungen - Agenten arbeiten nur in eigenem work-Verzeichnis - Absolute Pfade werden übergeben - Dateien-UI zeigt Agent-Work-Folders 💬 Chat-System überarbeitet - Echte Agent-Ausführung statt Mock-Responses - Server-Sent Events für Live-Streaming - Session-basierte Chat-History - Loading-Spinner und Status-Anzeigen - Automatisches Speichern in Session 🎭 Personality Integration - personality.md wird jetzt geladen - Persönlichkeit vor Systemprompt eingefügt - Gilt für alle: Chat, Tasks, Orchestrator, Email-Poller ✨ Weitere Verbesserungen - Alle Agenten nutzen execute_agent_task() zentral - Memory-Speicherung nach jedem Task - Work-Files in Datei-Verwaltung sichtbar - System-Dateien ausgeblendet - API-Route für Agent-Work-Dateien
This commit is contained in:
parent
84b2fe3dd7
commit
93eb8c6d47
83 changed files with 1692 additions and 1517 deletions
|
|
@ -14,7 +14,7 @@
|
|||
<span>💬 Neue Anfrage</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/chat">
|
||||
<form id="chatForm" onsubmit="sendChat(event)">
|
||||
<div class="mb-3">
|
||||
<label for="agent" class="form-label">Agent</label>
|
||||
<select class="form-select" id="agent" name="agent" required>
|
||||
|
|
@ -29,7 +29,11 @@
|
|||
<textarea class="form-control" id="prompt" name="prompt" rows="5"
|
||||
placeholder="Was soll der Agent erledigen?" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Absenden</button>
|
||||
<button type="submit" class="btn btn-primary w-100" id="sendBtn">
|
||||
<span id="sendBtnText">Absenden</span>
|
||||
<span id="sendBtnSpinner" class="d-none">⏳ Verarbeite...</span>
|
||||
</button>
|
||||
<div id="chatStatus" class="mt-2 text-center" style="font-size:0.875rem;"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -68,3 +72,163 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let eventSource = null;
|
||||
|
||||
function sendChat(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = document.getElementById('chatForm');
|
||||
const agent = document.getElementById('agent').value;
|
||||
const prompt = document.getElementById('prompt').value.trim();
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const sendBtnText = document.getElementById('sendBtnText');
|
||||
const sendBtnSpinner = document.getElementById('sendBtnSpinner');
|
||||
const chatStatus = document.getElementById('chatStatus');
|
||||
const chatContainer = document.getElementById('chatContainer');
|
||||
|
||||
if (!agent || !prompt) {
|
||||
chatStatus.innerHTML = '<span style="color:var(--danger);">Bitte Agent und Anfrage auswählen</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Button deaktivieren
|
||||
sendBtn.disabled = true;
|
||||
sendBtnText.classList.add('d-none');
|
||||
sendBtnSpinner.classList.remove('d-none');
|
||||
chatStatus.innerHTML = '<span style="color:var(--info);">Anfrage wird gesendet...</span>';
|
||||
|
||||
// EventSource für Streaming
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
}
|
||||
|
||||
// Anfrage per fetch senden
|
||||
fetch('/chat/send', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ agent: agent, prompt: prompt })
|
||||
})
|
||||
.then(response => {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let currentResponse = '';
|
||||
let currentTimestamp = new Date().toLocaleString('de-DE', {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
});
|
||||
let currentAgent = '';
|
||||
|
||||
// Neue Chat-Message erstellen
|
||||
const chatMessage = document.createElement('div');
|
||||
chatMessage.className = 'chat-message';
|
||||
chatMessage.innerHTML = `
|
||||
<div class="chat-timestamp">
|
||||
${currentTimestamp}
|
||||
<span class="badge bg-primary" id="chatAgentBadge">...</span>
|
||||
</div>
|
||||
<div class="chat-prompt"><strong>Sie:</strong> ${escapeHtml(prompt)}</div>
|
||||
<div class="chat-response mt-1"><strong>Antwort:</strong> <span id="chatResponseText">⏳ Warte auf Antwort...</span></div>
|
||||
`;
|
||||
|
||||
// Placeholder entfernen falls vorhanden
|
||||
const placeholder = chatContainer.querySelector('.text-center.py-5');
|
||||
if (placeholder) {
|
||||
placeholder.remove();
|
||||
}
|
||||
|
||||
chatContainer.appendChild(chatMessage);
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
|
||||
const responseText = document.getElementById('chatResponseText');
|
||||
const agentBadge = document.getElementById('chatAgentBadge');
|
||||
|
||||
function processChunk() {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
// Stream beendet
|
||||
sendBtn.disabled = false;
|
||||
sendBtnText.classList.remove('d-none');
|
||||
sendBtnSpinner.classList.add('d-none');
|
||||
chatStatus.innerHTML = '<span style="color:var(--success);">✓ Antwort erhalten</span>';
|
||||
setTimeout(() => { chatStatus.innerHTML = ''; }, 3000);
|
||||
|
||||
// Formular zurücksetzen
|
||||
document.getElementById('prompt').value = '';
|
||||
|
||||
// Nach kurzer Verzögerung Seite neu laden für aktualisierte History
|
||||
setTimeout(() => { location.reload(); }, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop();
|
||||
|
||||
lines.forEach(line => {
|
||||
if (line.startsWith('data: ')) {
|
||||
try {
|
||||
const data = JSON.parse(line.substring(6));
|
||||
|
||||
if (data.type === 'agent_selected') {
|
||||
currentAgent = data.agent;
|
||||
agentBadge.textContent = currentAgent;
|
||||
} else if (data.type === 'processing') {
|
||||
chatStatus.innerHTML = `<span style="color:var(--info);">${data.message}</span>`;
|
||||
} else if (data.type === 'response') {
|
||||
currentResponse = data.text;
|
||||
responseText.textContent = currentResponse;
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
} else if (data.type === 'complete') {
|
||||
chatStatus.innerHTML = `<span style="color:var(--success);">${data.message}</span>`;
|
||||
|
||||
// In Session speichern
|
||||
fetch('/chat/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
timestamp: data.timestamp,
|
||||
agent: currentAgent,
|
||||
agent_key: agent,
|
||||
prompt: prompt,
|
||||
response: data.response
|
||||
})
|
||||
});
|
||||
|
||||
} else if (data.type === 'error') {
|
||||
responseText.innerHTML = `<span style="color:var(--danger);">⚠️ Fehler: ${escapeHtml(data.message)}</span>`;
|
||||
chatStatus.innerHTML = `<span style="color:var(--danger);">Fehler aufgetreten</span>`;
|
||||
sendBtn.disabled = false;
|
||||
sendBtnText.classList.remove('d-none');
|
||||
sendBtnSpinner.classList.add('d-none');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Parse error:', e, line);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
processChunk();
|
||||
});
|
||||
}
|
||||
|
||||
processChunk();
|
||||
})
|
||||
.catch(error => {
|
||||
chatStatus.innerHTML = `<span style="color:var(--danger);">⚠️ ${error.message}</span>`;
|
||||
sendBtn.disabled = false;
|
||||
sendBtnText.classList.remove('d-none');
|
||||
sendBtnSpinner.classList.add('d-none');
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue