/**
* ChatGPT Assistent Modul
* Verwaltet die Interaktion mit der OpenAI API und die Benutzeroberfläche des Assistenten
*/
class ChatGPTAssistant {
constructor() {
this.messages = [];
this.isOpen = false;
this.isLoading = false;
this.container = null;
this.chatHistory = null;
this.inputField = null;
this.suggestionArea = null;
this.maxRetries = 2;
this.retryCount = 0;
this.markdownParser = null;
this.initializeMarkdownParser();
}
/**
* Initialisiert den Markdown-Parser
*/
async initializeMarkdownParser() {
// Dynamisch marked.js laden, wenn noch nicht vorhanden
if (!window.marked) {
try {
// Prüfen, ob marked.js bereits im Dokument geladen ist
if (!document.querySelector('script[src*="marked"]')) {
// Falls nicht, Script-Tag erstellen und einfügen
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
script.async = true;
// Promise erstellen, das resolved wird, wenn das Script geladen wurde
await new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
console.log('Marked.js erfolgreich geladen');
}
// Marked konfigurieren
this.markdownParser = window.marked;
this.markdownParser.setOptions({
gfm: true,
breaks: true,
sanitize: true,
smartLists: true,
smartypants: true
});
} catch (error) {
console.error('Fehler beim Laden von marked.js:', error);
// Fallback-Parser, der nur einfache Absätze erkennt
this.markdownParser = {
parse: (text) => {
return text.split('\n').map(line => {
if (line.trim() === '') return '
';
return `
${line}
`; }).join(''); } }; } } else { // Marked ist bereits geladen this.markdownParser = window.marked; } } /** * Initialisiert den Assistenten und fügt die UI zum DOM hinzu */ init() { // Assistent-Container erstellen this.createAssistantUI(); // Event-Listener hinzufügen this.setupEventListeners(); // Ersten Willkommensnachricht anzeigen this.addMessage("assistant", "Hallo! Ich bin dein KI-Assistent (4o-mini) und habe Zugriff auf die Wissensdatenbank. Wie kann ich dir helfen?\n\nDu kannst mir Fragen über:\n- **Gedanken** in der Datenbank\n- **Kategorien** und Wissenschaftsbereiche\n- **Mindmaps** und Wissensverknüpfungen\n\nstellen."); // Vorschläge anzeigen this.showSuggestions([ "Zeige mir Gedanken zur künstlichen Intelligenz", "Welche Kategorien gibt es in der Datenbank?", "Suche nach Mindmaps zum Thema Informatik" ]); console.log('KI-Assistent initialisiert!'); } /** * Erstellt die UI-Elemente für den Assistenten */ createAssistantUI() { // Hauptcontainer erstellen this.container = document.createElement('div'); this.container.id = 'chatgpt-assistant'; this.container.className = 'fixed bottom-4 right-4 z-50 flex flex-col'; // Button zum Öffnen/Schließen des Assistenten const toggleButton = document.createElement('button'); toggleButton.id = 'assistant-toggle'; toggleButton.className = 'ml-auto bg-primary-600 hover:bg-primary-700 text-white rounded-full p-3 shadow-lg transition-all duration-300 mb-2'; toggleButton.innerHTML = ''; // Chat-Container const chatContainer = document.createElement('div'); chatContainer.id = 'assistant-chat'; chatContainer.className = 'bg-white dark:bg-dark-800 rounded-lg shadow-xl overflow-hidden transition-all duration-300 w-80 md:w-96 max-h-0 opacity-0'; // Chat-Header const header = document.createElement('div'); header.className = 'bg-primary-600 text-white p-3 flex items-center justify-between'; header.innerHTML = `${line}
`; }).join(''); } } else { // Für Benutzernachrichten einfache Formatierung formattedText = text.split('\n').map(line => { if (line.trim() === '') return '${line}
`; }).join(''); } bubble.innerHTML = formattedText; messageEl.appendChild(bubble); if (this.chatHistory) { this.chatHistory.appendChild(messageEl); // Scroll zum Ende des Verlaufs this.chatHistory.scrollTop = this.chatHistory.scrollHeight; } } /** * Zeigt Vorschläge als klickbare Pills an * @param {string[]} suggestions - Liste von Vorschlägen */ showSuggestions(suggestions) { if (!this.suggestionArea) return; // Vorherige Vorschläge entfernen this.suggestionArea.innerHTML = ''; if (suggestions && suggestions.length > 0) { suggestions.forEach(suggestion => { const pill = document.createElement('button'); pill.className = 'suggestion-pill text-sm bg-gray-200 dark:bg-dark-600 hover:bg-gray-300 dark:hover:bg-dark-500 text-gray-800 dark:text-gray-200 rounded-full px-3 py-1 mb-2 transition-colors'; pill.textContent = suggestion; this.suggestionArea.appendChild(pill); }); this.suggestionArea.classList.remove('hidden'); } else { this.suggestionArea.classList.add('hidden'); } } /** * Sendet die Benutzernachricht an den Server und zeigt die Antwort an */ async sendMessage() { if (!this.inputField) return; const userInput = this.inputField.value.trim(); if (!userInput || this.isLoading) return; // Vorschläge ausblenden if (this.suggestionArea) { this.suggestionArea.classList.add('hidden'); } // Benutzernachricht anzeigen this.addMessage('user', userInput); // Eingabefeld zurücksetzen this.inputField.value = ''; // Ladeindikator anzeigen this.isLoading = true; this.showLoadingIndicator(); try { console.log('Sende Anfrage an KI-Assistent API...'); // Anfrage an den Server senden const response = await fetch('/api/assistant', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ messages: this.messages }), cache: 'no-cache', // Kein Cache verwenden credentials: 'same-origin' // Cookies senden }); // Ladeindikator entfernen this.removeLoadingIndicator(); if (!response.ok) { throw new Error(`Serverfehler: ${response.status} ${response.statusText}`); } const data = await response.json(); console.log('Antwort erhalten:', data); // Antwort anzeigen if (data.response) { this.addMessage('assistant', data.response); // Neue Vorschläge basierend auf dem aktuellen Kontext anzeigen this.generateContextualSuggestions(); // Erfolgreiche Anfrage zurücksetzen this.retryCount = 0; } else if (data.error) { this.addMessage('assistant', `Fehler: ${data.error}`); } else { throw new Error('Unerwartetes Antwortformat vom Server'); } } catch (error) { console.error('Fehler bei der Kommunikation mit dem Assistenten:', error); // Ladeindikator entfernen, falls noch vorhanden this.removeLoadingIndicator(); // Fehlermeldung anzeigen oder Wiederholungsversuch starten if (this.retryCount < this.maxRetries) { this.retryCount++; this.addMessage('assistant', 'Es gab ein Problem mit der Anfrage. Ich versuche es erneut...'); // Kurze Verzögerung vor dem erneuten Versuch setTimeout(() => { // Letzte Benutzernachricht aus dem Messages-Array entfernen const lastUserMessage = this.messages[this.messages.length - 2].content; this.messages = this.messages.slice(0, -2); // Entferne Benutzernachricht und Fehlermeldung // Erneuter Versand mit gleicher Nachricht this.inputField.value = lastUserMessage; this.sendMessage(); }, 1500); } else { // Maximale Anzahl an Wiederholungsversuchen erreicht this.addMessage('assistant', 'Es tut mir leid, aber es gab ein Problem bei der Verarbeitung deiner Anfrage. Bitte versuche es später noch einmal.'); this.retryCount = 0; // Zurücksetzen für die nächste Anfrage } } finally { this.isLoading = false; } } /** * Generiert kontextbasierte Vorschläge basierend auf dem aktuellen Chat-Verlauf */ generateContextualSuggestions() { // Basierend auf letzter Antwort des Assistenten, verschiedene Vorschläge generieren const lastAssistantMessage = this.messages.findLast(msg => msg.role === 'assistant')?.content || ''; let suggestions = []; // Intelligente Vorschläge basierend auf Kontext if (lastAssistantMessage.includes('Künstliche Intelligenz') || lastAssistantMessage.includes('KI ') || lastAssistantMessage.includes('AI ')) { suggestions = [ "Wie wird KI in der Wissenschaft eingesetzt?", "Zeige mir Gedanken zum maschinellen Lernen", "Was ist der Unterschied zwischen KI und ML?" ]; } else if (lastAssistantMessage.includes('Kategorie') || lastAssistantMessage.includes('Kategorien')) { suggestions = [ "Zeige mir die Unterkategorien", "Welche Gedanken gehören zu dieser Kategorie?", "Liste alle Wissenschaftskategorien auf" ]; } else if (lastAssistantMessage.includes('Mindmap') || lastAssistantMessage.includes('Visualisierung')) { suggestions = [ "Wie kann ich eine eigene Mindmap erstellen?", "Zeige mir Beispiele für Mindmaps", "Wie funktionieren die Verbindungen in Mindmaps?" ]; } else { // Standardvorschläge suggestions = [ "Erzähle mir mehr dazu", "Gibt es Beispiele dafür?", "Wie kann ich diese Information nutzen?" ]; } this.showSuggestions(suggestions); } /** * Zeigt einen Ladeindikator im Chat an */ showLoadingIndicator() { if (!this.chatHistory) return; // Entferne vorhandenen Ladeindikator (falls vorhanden) this.removeLoadingIndicator(); const loadingEl = document.createElement('div'); loadingEl.id = 'assistant-loading'; loadingEl.className = 'flex justify-start'; const bubble = document.createElement('div'); bubble.className = 'bg-gray-100 dark:bg-dark-700 text-gray-800 dark:text-white rounded-lg py-2 px-3'; bubble.innerHTML = '