1610 lines
No EOL
298 KiB
JSON
1610 lines
No EOL
298 KiB
JSON
{
|
|
"id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"mandate_id": 1,
|
|
"user_id": 1,
|
|
"name": "Workflow 15.4.2025, 17:13:25",
|
|
"status": "completed",
|
|
"started_at": "2025-04-15T17:13:29.673655",
|
|
"last_activity": "2025-04-15T17:16:29.291433",
|
|
"prompt": "Bitte beschreibe mir das Frontend gemäss dem beiliegenden Code: Was ist das , wie funktioniert es und was kann ich damit an Mehrwert erreichen",
|
|
"messages": [
|
|
{
|
|
"id": "msg_6efe096a-a6be-4a07-9d21-66f1350ae4a9",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:13:29.771953",
|
|
"finished_at": null,
|
|
"sequence_no": 1,
|
|
"status": "pending",
|
|
"role": "user",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [
|
|
{
|
|
"id": "doc_c78ba9a3-beac-4bec-8f53-bddf81d98875",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_d182cf6b-2fa1-458d-85d7-e7413fe1f206",
|
|
"name": "prompts.js",
|
|
"content_type": "application/javascript",
|
|
"size": 9771,
|
|
"upload_date": "2025-04-15T17:13:30.167883"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\n * prompts.js\n * Modul für die Prompt-Vorlagen-Verwaltung\n * Angepasst ohne Workspace-Funktionalität\n */\n\nimport globalState from '../globalState.js';\nimport { loadModule } from '../module-loader.js';\n\n/**\n * Initialisierungsfunktion für das Prompts-Modul\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\n */\nfunction initPromptsModule(globalStateObj) {\n console.log(\"Initialisiere Prompts-Modul...\");\n \n // Sicherstellen, dass das generische Entitätsmodul verfügbar ist\n if (!window.genericEntityModule) {\n console.error(\"Fehler: Das generische Entitätsmodul ist nicht verfügbar.\");\n return;\n }\n \n // Generisches Entitätsmodul für Prompts initialisieren\n window.genericEntityModule.init(globalStateObj, {\n entityType: 'prompt',\n apiEndpoint: '/api/prompts',\n listContainerId: 'prompts-list',\n addButtonId: 'add-prompt-btn',\n \n // Benutzerdefinierte Render-Funktion für Prompts\n renderItem: function(prompt) {\n return `\n <div class=\"list-header\">\n <h4>${prompt.name || 'Unbenannter Prompt'}</h4>\n </div>\n <div class=\"prompt-content\">${prompt.content}</div>\n <div class=\"list-body\">\n ${prompt.created_at ? `Erstellt am: ${new Date(prompt.created_at).toLocaleString()}` : ''}\n </div>\n `;\n },\n \n // Transformiert die Formulardaten vor dem Speichern\n transformFormData: function(formData) {\n // Alte Workspace-Referenz entfernt\n \n // Sicherstellen, dass Content als String gespeichert wird\n if (formData.content) {\n formData.content = String(formData.content).trim();\n }\n \n return formData;\n },\n \n // Zusätzliche Aktionen für Prompts mit optimierten Icons\n getItemActions: function(prompt) {\n return [\n {\n text: 'Verwenden',\n className: 'entity-use-btn',\n html: '<i class=\"fas fa-play\"></i>',\n handler: async function(prompt) {\n // Zum Workflow wechseln und diesen Prompt verwenden\n await loadModule('workflow', globalStateObj);\n globalStateObj.actions.activateModule('workflow');\n \n // Kurze Verzögerung, damit das DOM aktualisiert werden kann\n setTimeout(() => {\n const promptInput = document.getElementById('prompt-input');\n if (promptInput) {\n promptInput.value = prompt.content;\n \n // Event auslösen, damit der Workflow-State aktualisiert wird\n const event = new Event('input', { bubbles: true });\n promptInput.dispatchEvent(event);\n }\n }, 300);\n }\n },\n {\n text: 'Kopieren',\n className: 'entity-copy-btn',\n html: '<i class=\"fas fa-copy\"></i>',\n handler: function(prompt) {\n copyPromptToClipboard(prompt);\n }\n }\n ];\n },\n \n // Callback nach Erstellung eines Elements\n onItemCreated: function(prompt) {\n showToast(`Prompt \"${prompt.name}\" erfolgreich erstellt`);\n \n // Globale Promptliste aktualisieren\n if (globalStateObj.mainView.availablePrompts) {\n globalStateObj.mainView.availablePrompts.push(prompt);\n }\n },\n \n // Callback nach Aktualisierung eines Elements\n onItemUpdated: function(prompt) {\n showToast(`Prompt \"${prompt.name}\" aktualisiert`);\n \n // Globale Promptliste aktualisieren\n if (globalStateObj.mainView.availablePrompts) {\n const index = globalStateObj.mainView.availablePrompts.findIndex(p => p.id === prompt.id);\n if (index !== -1) {\n globalStateObj.mainView.availablePrompts[index] = prompt;\n }\n }\n },\n \n // Callback nach Löschung eines Elements\n onItemDeleted: function(promptId) {\n showToast(\"Prompt gelöscht\");\n \n // Globale Promptliste aktualisieren\n if (globalStateObj.mainView.availablePrompts) {\n globalStateObj.mainView.availablePrompts = globalStateObj.mainView.availablePrompts.filter(\n p => p.id !== promptId\n );\n }\n }\n });\n \n // Event-Listener für Prompt-Kategorien, falls vorhanden\n const categoryFilters = document.querySelectorAll('.prompt-category-filter');\n if (categoryFilters && categoryFilters.length > 0) {\n categoryFilters.forEach(filter => {\n filter.addEventListener('click', () => {\n const category = filter.getAttribute('data-category');\n filterPromptsByCategory(category);\n });\n });\n }\n \n console.log(\"Prompts-Modul erfolgreich initialisiert.\");\n}\n\n/**\n * Zeigt eine Toast-Nachricht an\n * @param {string} message - Anzuzeigende Nachricht\n */\nfunction showToast(message) {\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showToast) {\n window.appUtils.ui.showToast(message);\n } else if (window.globalUtils && window.globalUtils.showError) {\n window.globalUtils.showToast(message);\n } else {\n //console.log(message);\n }\n}\n\n/**\n * Kopiert den Prompt-Inhalt in die Zwischenablage\n * @param {Object} prompt - Der zu kopierende Prompt\n */\nfunction copyPromptToClipboard(prompt) {\n if (!prompt || !prompt.content) {\n showToast(\"Kein Inhalt zum Kopieren vorhanden\");\n return;\n }\n \n try {\n navigator.clipboard.writeText(prompt.content)\n .then(() => {\n showToast(\"Prompt in die Zwischenablage kopiert\");\n })\n .catch(err => {\n console.error('Fehler beim Kopieren:', err);\n \n // Fallback für Browser ohne Clipboard API\n const textarea = document.createElement('textarea');\n textarea.value = prompt.content;\n textarea.style.position = 'fixed';\n document.body.appendChild(textarea);\n textarea.focus();\n textarea.select();\n \n try {\n document.execCommand('copy');\n showToast(\"Prompt in die Zwischenablage kopiert\");\n } catch (err) {\n console.error('Fallback-Kopieren fehlgeschlagen:', err);\n showToast(\"Kopieren fehlgeschlagen. Bitte kopieren Sie den Text manuell.\");\n }\n \n document.body.removeChild(textarea);\n });\n } catch (error) {\n console.error(\"Fehler beim Kopieren:\", error);\n showToast(\"Kopieren fehlgeschlagen\");\n }\n}\n\n/**\n * Filtert Prompts nach Kategorie\n * @param {string} category - Die Kategorie-ID\n */\nfunction filterPromptsByCategory(category) {\n // Alle Kategorie-Filter-Buttons aktualisieren\n const allFilters = document.querySelectorAll('.prompt-category-filter');\n allFilters.forEach(filter => {\n if (filter.getAttribute('data-category') === category) {\n filter.classList.add('active');\n } else {\n filter.classList.remove('active');\n }\n });\n \n // Alle Prompt-Elemente durchgehen\n const promptElements = document.querySelectorAll('.prompt-item');\n promptElements.forEach(element => {\n const promptCategory = element.getAttribute('data-category');\n \n if (category === 'all' || promptCategory === category) {\n element.style.display = '';\n } else {\n element.style.display = 'none';\n }\n });\n}\n\n/**\n * Erstellt einen neuen Prompt mit Default-Werten\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\n * @returns {Promise<Object>} - Der erstellte Prompt\n */\nasync function createDefaultPrompt(globalStateObj) {\n try {\n // Default-Prompt erstellen (ohne Workspace-Referenz)\n const defaultPrompt = {\n name: \"Neuer Prompt\",\n content: \"Geben Sie hier einen Prompt ein...\",\n category: \"general\"\n };\n\n // An die API senden über globalUtils\n const newPrompt = await window.globalUtils.writeApiData('/api/prompts', defaultPrompt);\n\n // Zur globalen Liste hinzufügen\n if (globalStateObj.mainView.availablePrompts) {\n globalStateObj.mainView.availablePrompts.push(newPrompt);\n }\n\n // UI aktualisieren\n if (window.genericEntityModule) {\n window.genericEntityModule.loadAndRenderItems();\n }\n\n return newPrompt;\n } catch (error) {\n console.error(\"Fehler beim Erstellen des Default-Prompts:\", error);\n if (window.globalUtils && window.globalUtils.showError) {\n window.globalUtils.showError(\"Fehler beim Erstellen\", error.message);\n }\n throw error;\n }\n}\n\n// Prompts-Modul-Schnittstelle definieren und exportieren\nwindow.initPromptsModule = initPromptsModule;\n\n// Export für ES Module\nexport {\n initPromptsModule,\n copyPromptToClipboard,\n filterPromptsByCategory,\n createDefaultPrompt\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_c21a83e0-454c-40cb-93bd-8c3c3332e049",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_40ff7a7f-d490-43c5-8792-b3bd270a5859",
|
|
"name": "users.js",
|
|
"content_type": "application/javascript",
|
|
"size": 11958,
|
|
"upload_date": "2025-04-15T17:13:30.167883"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * users.js\r\n * Modul für die Benutzerverwaltung\r\n * Angepasst an das überarbeitete Backend\r\n */\r\n\r\n/**\r\n * Initialisierungsfunktion für das Users-Modul\r\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\r\n */\r\nfunction initUsersModule(globalStateObj) {\r\n console.log(\"Initialisiere Users-Modul...\");\r\n \r\n // Prüfen, ob der Benutzer die erforderlichen Rechte hat (nur Admin und SysAdmin)\r\n if (!globalStateObj.user || (!globalStateObj.user.isAdmin && !globalStateObj.user.isSysAdmin)) {\r\n // Benutzer-Sektion ausblenden oder Hinweis anzeigen\r\n const usersView = document.getElementById('users-view');\r\n if (usersView) {\r\n usersView.innerHTML = `\r\n <h2>Benutzer</h2>\r\n <div class=\"card\">\r\n <div class=\"alert alert-warning\">\r\n <i class=\"fas fa-exclamation-triangle\"></i>\r\n Sie benötigen Admin- oder SysAdmin-Rechte, um Benutzer zu verwalten.\r\n </div>\r\n </div>\r\n `;\r\n }\r\n console.warn(\"Keine ausreichenden Berechtigungen für die Benutzerverwaltung.\");\r\n return;\r\n }\r\n \r\n // Sicherstellen, dass das generische Entitätsmodul verfügbar ist\r\n if (!window.genericEntityModule) {\r\n console.error(\"Fehler: Das generische Entitätsmodul ist nicht verfügbar.\");\r\n return;\r\n }\r\n \r\n // Generisches Entitätsmodul für Benutzer initialisieren\r\n window.genericEntityModule.init(globalStateObj, {\r\n entityType: 'user',\r\n apiEndpoint: '/api/users', // Anpassung an neues Backend\r\n listContainerId: 'users-list-container',\r\n addButtonId: 'add-user-btn',\r\n \r\n // Benutzerdefinierte Render-Funktion für Benutzer\r\n renderItem: function(user) {\r\n // Badge für Benutzerrolle mit entsprechender Farbe\r\n let privilegeBadge = '';\r\n switch(user.privilege) {\r\n case 'sysadmin':\r\n privilegeBadge = '<span class=\"badge badge-purple\">SysAdmin</span>';\r\n break;\r\n case 'admin':\r\n privilegeBadge = '<span class=\"badge badge-blue\">Admin</span>';\r\n break;\r\n default:\r\n privilegeBadge = '<span class=\"badge badge-gray\">User</span>';\r\n }\r\n\r\n // Status-Badge (aktiv/deaktiviert)\r\n const statusBadge = user.disabled ? \r\n '<span class=\"badge badge-red\">Deaktiviert</span>' : \r\n '<span class=\"badge badge-green\">Aktiv</span>';\r\n\r\n // Der aktuelle Benutzer soll sich erkennen\r\n const isCurrentUser = user.id === globalStateObj.user.id;\r\n const currentUserIndicator = isCurrentUser ? \r\n '<span class=\"current-user-indicator\">(Sie)</span>' : '';\r\n\r\n // Prüfen, ob der Benutzer zum gleichen Mandanten gehört (relevant für Admins)\r\n const sameMandateUser = globalStateObj.user.mandate_id === user.mandate_id;\r\n \r\n // Mandanten-Info nur für SysAdmin anzeigen\r\n const mandateInfo = globalStateObj.user.isSysAdmin && user.mandate_id ? \r\n `<div class=\"user-mandate\">Mandant: ${user.mandate_name || user.mandate_id}</div>` : '';\r\n\r\n return `\r\n <div class=\"list-header\">\r\n <div>\r\n <h4>${user.username || 'Unbenannter Benutzer'} ${currentUserIndicator}</h4>\r\n </div>\r\n <div class=\"user-badges\">\r\n ${privilegeBadge}\r\n ${statusBadge}\r\n </div>\r\n </div>\r\n <div class=\"list-body\">\r\n ${user.full_name ? `<div class=\"user-name\">${user.full_name}</div>` : ''}\r\n ${user.email ? `<div class=\"user-email\">${user.email}</div>` : ''}\r\n ${mandateInfo}\r\n ${user.language ? `<div class=\"user-language\">Sprache: ${user.language}</div>` : ''}\r\n ${user.created_at ? `Erstellt am: ${new Date(user.created_at).toLocaleString()}` : ''}\r\n </div>\r\n `;\r\n },\r\n \r\n // Behandlung spezieller Attributformate\r\n transformFormData: function(formData) {\r\n // Wenn Passwort leer ist und es sich um ein Update handelt (ID vorhanden),\r\n // entfernen wir das Passwortfeld, damit das bestehende nicht überschrieben wird\r\n if (formData.id && (!formData.password || formData.password.trim() === '')) {\r\n delete formData.password;\r\n }\r\n \r\n // Boolean-Wert für \"disabled\" korrekt umwandeln\r\n if (formData.hasOwnProperty('disabled') && typeof formData.disabled === 'string') {\r\n formData.disabled = formData.disabled.toLowerCase() === 'true';\r\n }\r\n \r\n // Mandate ID korrekt als Zahl verarbeiten\r\n if (formData.mandate_id && typeof formData.mandate_id === 'string') {\r\n formData.mandate_id = parseInt(formData.mandate_id, 10);\r\n }\r\n \r\n return formData;\r\n },\r\n \r\n // Zusätzliche Aktionen für Benutzer\r\n getItemActions: function(user) {\r\n const actions = [];\r\n \r\n // Prüfen, ob der aktuelle Benutzer SysAdmin ist\r\n const isSysAdmin = globalStateObj.user.privilege === 'sysadmin';\r\n \r\n // Prüfen, ob es sich um den eigenen Account handelt\r\n const isCurrentUser = user.id === globalStateObj.user.id;\r\n \r\n // Benutzer aktivieren/deaktivieren (nur für Admins und SysAdmins, nicht für den eigenen Account)\r\n if (!isCurrentUser) {\r\n if (user.disabled) {\r\n actions.push({\r\n text: 'Aktivieren',\r\n className: 'entity-activate-btn',\r\n html: '<i class=\"fas fa-check-circle\"></i>',\r\n handler: function(user) {\r\n toggleUserStatus(user, false);\r\n }\r\n });\r\n } else {\r\n actions.push({\r\n text: 'Deaktivieren',\r\n className: 'entity-deactivate-btn',\r\n html: '<i class=\"fas fa-ban\"></i>',\r\n handler: function(user) {\r\n toggleUserStatus(user, true);\r\n }\r\n });\r\n }\r\n }\r\n \r\n // Passwort zurücksetzen\r\n actions.push({\r\n text: 'Passwort zurücksetzen',\r\n className: 'entity-reset-btn',\r\n html: '<i class=\"fas fa-key\"></i>',\r\n handler: function(user) {\r\n resetUserPassword(user);\r\n }\r\n });\r\n \r\n return actions;\r\n },\r\n \r\n // Callback nach Erstellung eines Elements\r\n onItemCreated: function(user) {\r\n showToast(`Benutzer \"${user.username}\" erstellt`);\r\n },\r\n \r\n // Callback nach Aktualisierung eines Elements\r\n onItemUpdated: function(user) {\r\n showToast(`Benutzer \"${user.username}\" aktualisiert`);\r\n },\r\n \r\n // Callback nach Löschung eines Elements\r\n onItemDeleted: function(userId) {\r\n showToast(`Benutzer gelöscht`);\r\n }\r\n });\r\n \r\n console.log(\"Users-Modul erfolgreich initialisiert.\");\r\n}\r\n\r\n/**\r\n * Benutzer aktivieren/deaktivieren\r\n * @param {Object} user - Der Benutzer\r\n * @param {boolean} disabled - Neuer Status (true = deaktiviert, false = aktiviert)\r\n */\r\nasync function toggleUserStatus(user, disabled) {\r\n try {\r\n // Status ändern via API\r\n const endpoint = `/api/users/${user.id}`;\r\n const response = await window.globalUtils.writeApiData(endpoint, { disabled });\r\n \r\n if (response) {\r\n showToast(`Benutzer \"${user.username}\" ${disabled ? 'deaktiviert' : 'aktiviert'}`);\r\n \r\n // Liste neu laden\r\n if (window.genericEntityModule) {\r\n window.genericEntityModule.loadAndRenderItems();\r\n }\r\n }\r\n } catch (error) {\r\n console.error(`Fehler beim Ändern des Benutzerstatus:`, error);\r\n showError(`Fehler beim Ändern des Benutzerstatus`, error.message);\r\n }\r\n}\r\n\r\n/**\r\n * Passwort für einen Benutzer zurücksetzen\r\n * @param {Object} user - Der Benutzer\r\n */\r\nasync function resetUserPassword(user) {\r\n try {\r\n // Temporäres Passwort generieren\r\n const tempPassword = generateTemporaryPassword();\r\n \r\n // Passwort ändern via API - Angepasst an neues Backend\r\n const endpoint = `/api/users/${user.id}`;\r\n const response = await window.globalUtils.writeApiData(endpoint, { password: tempPassword });\r\n \r\n if (response) {\r\n alert(`Das Passwort für \"${user.username}\" wurde zurückgesetzt.\\n\\nTemporäres Passwort: ${tempPassword}\\n\\nBitte teilen Sie dieses Passwort sicher mit dem Benutzer.`);\r\n } else {\r\n throw new Error(\"Das Passwort konnte nicht zurückgesetzt werden.\");\r\n }\r\n } catch (error) {\r\n console.error(`Fehler beim Zurücksetzen des Passworts:`, error);\r\n showError(`Fehler beim Zurücksetzen des Passworts`, error.message);\r\n }\r\n}\r\n\r\n/**\r\n * Generiert ein temporäres Passwort\r\n * @returns {string} - Temporäres Passwort\r\n */\r\nfunction generateTemporaryPassword() {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()';\r\n let tempPassword = '';\r\n for (let i = 0; i < 12; i++) {\r\n tempPassword += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return tempPassword;\r\n}\r\n\r\n/**\r\n * Neuen Benutzer registrieren\r\n * @param {Object} userData - Die Benutzerdaten\r\n * @returns {Promise<Object>} - Der erstellte Benutzer\r\n */\r\nasync function registerUser(userData) {\r\n try {\r\n // API-Endpunkt für die Registrierung\r\n const endpoint = '/api/users/register';\r\n const response = await window.globalUtils.writeApiData(endpoint, userData);\r\n \r\n if (response) {\r\n showToast(`Benutzer \"${response.username}\" wurde erfolgreich registriert.`);\r\n }\r\n \r\n return response;\r\n } catch (error) {\r\n console.error(`Fehler bei der Benutzerregistrierung:`, error);\r\n showError(`Registrierungsfehler`, error.message);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt eine Toast-Nachricht an\r\n * @param {string} message - Anzuzeigende Nachricht\r\n */\r\nfunction showToast(message) {\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showToast) {\r\n window.appUtils.ui.showToast(message);\r\n } else if (window.globalUtils && window.globalUtils.showError) {\r\n window.globalUtils.showError(\"Info\", message);\r\n } else {\r\n console.log(message);\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt eine Fehlermeldung an\r\n * @param {string} title - Titel der Fehlermeldung\r\n * @param {string} message - Fehlermeldung\r\n */\r\nfunction showError(title, message) {\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showError) {\r\n window.appUtils.ui.showError(title, message);\r\n } else if (window.globalUtils && window.globalUtils.showError) {\r\n window.globalUtils.showError(title, message);\r\n } else {\r\n console.error(`${title}: ${message}`);\r\n }\r\n}\r\n\r\n// Users-Modul-Schnittstelle definieren und exportieren\r\nwindow.initUsersModule = initUsersModule;\r\n\r\n// Export für ES Module\r\nexport {\r\n initUsersModule,\r\n toggleUserStatus,\r\n resetUserPassword,\r\n registerUser\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_d96cd56b-ddc3-4a60-8239-2d28d9f2f6e8",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_2bddfba8-5017-48c0-bb48-545788365ce5",
|
|
"name": "files.js",
|
|
"content_type": "application/javascript",
|
|
"size": 7604,
|
|
"upload_date": "2025-04-15T17:13:30.167883"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\n * files.js\n * Modul für die Dateiverwaltung\n * Angepasst ohne Workspace-Funktionalität\n */\n\nimport globalState from '../globalState.js';\n\n/**\n * Initialisierungsfunktion für das Dateien-Modul\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\n */\nfunction initFilesModule(globalStateObj) {\n // DOM-Elemente referenzieren\n const filesFileInput = document.getElementById('files-file-input');\n const uploadBtn = document.getElementById('files-upload-btn');\n const filesList = document.getElementById('my-files-list');\n \n // Prüfen, ob alle erforderlichen DOM-Elemente vorhanden sind\n if (!filesFileInput || !uploadBtn || !filesList) {\n console.error('Erforderliche DOM-Elemente für das Files-Modul fehlen.');\n return;\n }\n \n // Generisches Entitätsmodul für Dateien initialisieren\n if (window.genericEntityModule) {\n window.genericEntityModule.init(globalStateObj, {\n entityType: 'file',\n apiEndpoint: '/api/files',\n listContainerId: 'my-files-list',\n addButtonId: 'files-upload-btn',\n \n // Benutzerdefinierte Render-Funktion für Dateien\n renderItem: function(file) {\n return `\n <div class=\"file-item-name\">\n <i class=\"fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}\"></i>\n <span>${file.name || 'Unbekannte Datei'}</span>\n </div>\n <div class=\"file-item-info\">\n <span>${file.size || ''}</span>\n <span>${new Date(file.upload_date || Date.now()).toLocaleString()}</span>\n </div>\n `;\n },\n \n // Transformiert die Formulardaten vor dem Speichern\n transformFormData: function(formData) {\n // Alte Workspace-Referenz entfernt\n return formData;\n },\n \n // Zusätzliche Aktionen für Dateien\n getItemActions: function(file) {\n return [\n {\n text: 'Vorschau',\n className: 'entity-view-btn',\n html: '<i class=\"fas fa-eye\"></i>',\n handler: function(file) {\n // Implementierung einer Dateivorschau\n showFilePreview(file);\n }\n },\n {\n text: 'Herunterladen',\n className: 'entity-download-btn',\n html: '<i class=\"fas fa-download\"></i>',\n handler: function(file) {\n downloadFile(file);\n }\n }\n ];\n },\n \n // Callback nach Erstellung eines Elements\n onItemCreated: function(file) {\n showUploadSuccess(file.name);\n \n // Globale Dateiliste aktualisieren\n if (globalStateObj.mainView.availableFiles) {\n globalStateObj.mainView.availableFiles.push(file);\n }\n },\n \n // Callback nach Löschung eines Elements\n onItemDeleted: function(fileId) {\n showToast(\"Datei gelöscht\");\n \n // Globale Dateiliste aktualisieren\n if (globalStateObj.mainView.availableFiles) {\n globalStateObj.mainView.availableFiles = globalStateObj.mainView.availableFiles.filter(\n f => f.id !== fileId\n );\n }\n }\n });\n } else {\n console.error('genericEntityModule nicht verfügbar. Bitte stellen Sie sicher, dass form-generic-entity.js geladen wurde.');\n return;\n }\n \n // Zusätzliche Event-Listener für Datei-Upload\n if (filesFileInput && uploadBtn) {\n // Datei-Upload-Button\n uploadBtn.addEventListener('click', () => filesFileInput.click());\n \n // Datei-Auswahl-Event\n filesFileInput.addEventListener('change', handleFileUpload);\n }\n \n // Log zur Bestätigung der Initialisierung\n console.log('Files-Modul erfolgreich initialisiert.');\n}\n\n/**\n * Handler für Datei-Uploads\n * @param {Event} event - Das Change-Event\n */\nfunction handleFileUpload(event) {\n const filesFileInput = event.target;\n \n if (filesFileInput.files.length > 0) {\n for (const file of filesFileInput.files) {\n uploadFile(file);\n }\n \n // Eingabefeld zurücksetzen\n filesFileInput.value = \"\";\n }\n}\n\n/**\n * Lädt eine Datei hoch\n * @param {File} file - Die hochzuladende Datei\n */\nasync function uploadFile(file) {\n try {\n const formData = new FormData();\n formData.append('file', file);\n \n // API-Aufruf für den Upload\n const newFile = await window.globalUtils.uploadFile('/api/files/upload', formData);\n \n // Nach erfolgreichem Upload die Liste aktualisieren\n if (window.genericEntityModule) {\n window.genericEntityModule.loadAndRenderItems();\n }\n \n // Auch den globalen State aktualisieren\n if (globalState.mainView.availableFiles) {\n globalState.mainView.availableFiles.push(newFile);\n }\n \n // Erfolg anzeigen\n showUploadSuccess(newFile.name);\n } catch (error) {\n console.error(\"Fehler beim Hochladen der Datei:\", error);\n showUploadError(error.message);\n }\n}\n\n/**\n * Zeigt eine Vorschau der Datei\n * @param {Object} file - Die anzuzeigende Datei\n */\nfunction showFilePreview(file) {\n // Diese Funktion könnte eine Modalansicht mit Dateidetails öffnen\n showToast(`Dateiinformationen: ${file.name}, Typ: ${file.type}, Größe: ${file.size}`);\n}\n\n/**\n * Lädt eine Datei herunter\n * @param {Object} file - Die herunterzuladende Datei\n */\nfunction downloadFile(file) {\n window.open(`/api/files/${file.id}/download`, '_blank');\n}\n\n/**\n * Zeigt eine allgemeine Toast-Nachricht an\n * @param {string} message - Die anzuzeigende Nachricht\n */\nfunction showToast(message) {\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showToast) {\n window.appUtils.ui.showToast(message);\n } else if (window.globalUtils && window.globalUtils.showError) {\n window.globalUtils.showError(\"Info\", message);\n } else {\n console.log(message);\n }\n}\n\n/**\n * Zeigt eine Erfolgsmeldung für erfolgreichen Upload\n * @param {string} fileName - Der Name der hochgeladenen Datei\n */\nfunction showUploadSuccess(fileName) {\n showToast(`Datei \"${fileName}\" erfolgreich hochgeladen.`);\n}\n\n/**\n * Zeigt eine Fehlermeldung für fehlgeschlagenen Upload\n * @param {string} errorMessage - Die Fehlermeldung\n */\nfunction showUploadError(errorMessage) {\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showError) {\n window.appUtils.ui.showError(\"Fehler beim Hochladen\", errorMessage);\n } else if (window.globalUtils && window.globalUtils.showError) {\n window.globalUtils.showError(\"Fehler beim Hochladen\", errorMessage);\n } else {\n console.error(`Fehler beim Hochladen der Datei: ${errorMessage}`);\n }\n}\n\n// Modul-Initialisierung exportieren\nwindow.initFilesModule = initFilesModule;\n\n// Export für ES Module\nexport {\n initFilesModule,\n handleFileUpload,\n uploadFile,\n showFilePreview,\n downloadFile\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_bd1821c3-a08b-4339-8d87-dea667aec607",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_7e9a8f0a-79c9-4c36-8932-e9c594ab4aae",
|
|
"name": "workflow.js",
|
|
"content_type": "application/javascript",
|
|
"size": 1971,
|
|
"upload_date": "2025-04-15T17:13:30.168891"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\n * workflow.js\n * Hauptmodul für Workflow-Funktionalitäten - Refaktorierte Version\n * Zentrale Koordination aller Workflow-Komponenten\n */\n\n// Importe der Module\nimport * as WorkflowUI from './workflow_ui.js';\nimport * as WorkflowData from './workflow_data.js';\nimport * as WorkflowFeatures from './workflow_features.js';\nimport * as WorkflowCoordination from './workflow_coordination.js'; // Add new import\n\n// Gemeinsam genutzter Workflow-Zustand\nconst workflowState = {\n isRunning: false,\n isStoppingWorkflow: false,\n currentWorkflowId: null,\n lastPolledLogId: null,\n lastPolledMessageId: null,\n activeAgent: null,\n logs: [],\n chatMessages: [],\n finalPollingAfterUserWaiting: false // Neuer Zustand\n};\n\n/**\n * Initialisierungsfunktion für das gesamte Workflow-Modul\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\n */\nfunction initWorkflowModule(globalStateObj) {\n console.log(\"Initialisiere Workflow-Modul 1...\");\n \n // Pass the workflowState object as the first parameter\n WorkflowCoordination.initCoordination(workflowState, {\n ui: WorkflowUI,\n features: WorkflowFeatures,\n data: WorkflowData\n });\n\n // Dann die Benutzeroberfläche initialisieren\n WorkflowUI.initUI(workflowState, {\n onLayoutChange: WorkflowUI.handleLayoutChange,\n onResetWorkflow: WorkflowFeatures.resetWorkflow,\n onStopWorkflow: WorkflowFeatures.stopWorkflow\n });\n \n // Datenschicht initialisieren\n WorkflowData.initDataLayer(globalStateObj, workflowState);\n \n // Feature-Module initialisieren\n WorkflowFeatures.initFeatures(workflowState, {\n onWorkflowStarted: WorkflowFeatures.handleWorkflowStarted,\n onUserResponseSent: WorkflowFeatures.handleUserResponseSent,\n onFilePreview: WorkflowUI.previewFile\n });\n \n console.log(\"Workflow-Modul erfolgreich initialisiert.\");\n}\n\n\n// Modul exportieren\nexport {\n initWorkflowModule\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_188ea22f-c9a4-4ebf-9c20-f657a8aa75c8",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_765f731f-4eb7-4285-bc15-1c70cd5ae8c4",
|
|
"name": "workflow_data.js",
|
|
"content_type": "application/javascript",
|
|
"size": 33229,
|
|
"upload_date": "2025-04-15T17:13:30.168891"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * workflow_data.js\r\n * Datenmanagement und API-Kommunikation für das Workflow-Modul\r\n */\r\n\r\nimport * as WorkflowUI from './workflow_ui.js';\r\nimport * as WorkflowUtils from './workflow_utils.js';\r\nimport * as WorkflowCoordination from './workflow_coordination.js'; \r\n\r\n// Referenz auf den globalen Zustand\r\nlet globalState = null;\r\n\r\n// Referenz auf den Workflow-Zustand\r\nlet workflowState = null;\r\n\r\n// Datenstatistiken\r\nlet dataCounters = {\r\n sentDataSize: 0,\r\n receivedDataSize: 0\r\n};\r\n\r\n/**\r\n * Initialisiert die Datenverwaltungsschicht\r\n * @param {Object} globalStateObj - Globaler Anwendungszustand\r\n * @param {Object} workflowStateObj - Workflow-Modulzustand\r\n */\r\nfunction initDataLayer(globalStateObj, workflowStateObj) {\r\n console.log(\"Initialisiere Workflow-Datenlayer...\");\r\n \r\n globalState = globalStateObj;\r\n workflowState = workflowStateObj;\r\n \r\n console.log(\"Workflow-Datenlayer erfolgreich initialisiert\");\r\n}\r\n\r\n/**\r\n * Aktualisiert die Datenzähler für die Statistik\r\n * @param {number} sentBytes - Gesendete Bytes\r\n * @param {number} receivedBytes - Empfangene Bytes\r\n */\r\nfunction updateDataCounters(sentBytes, receivedBytes) {\r\n // Sicherstellen, dass wir mit gültigen Zahlen arbeiten\r\n let sentCount = 0;\r\n let receivedCount = 0;\r\n \r\n if (sentBytes && !isNaN(sentBytes)) {\r\n sentCount = Math.max(0, parseInt(sentBytes, 10));\r\n dataCounters.sentDataSize += sentCount;\r\n }\r\n \r\n if (receivedBytes && !isNaN(receivedBytes)) {\r\n receivedCount = Math.max(0, parseInt(receivedBytes, 10));\r\n dataCounters.receivedDataSize += receivedCount;\r\n }\r\n \r\n // UI aktualisieren\r\n WorkflowUI.updateDataStatistics(sentCount, receivedCount);\r\n}\r\n\r\n\r\n/**\r\n * Lädt eine Datei hoch oder verwendet eine bereits existierende Datei\r\n * und fügt sie zur entsprechenden Liste hinzu\r\n * Updated for better file deduplication and error handling\r\n * @param {File} file - Die hochzuladende Datei\r\n * @param {boolean} isInitial - Ob es sich um eine initiale Datei handelt\r\n * @returns {Promise} - Promise, das aufgelöst wird, wenn die Datei verarbeitet wurde\r\n */\r\nasync function uploadAndAddFile(file, isInitial = false) {\r\n try {\r\n // Always upload/process files as additional files\r\n let processedFile = null;\r\n\r\n // Check if file already exists with improved matching\r\n let existingFile = null;\r\n \r\n // First check by name and size (exact match)\r\n existingFile = window.globalState.mainView.availableFiles.find(\r\n f => f.name === file.name && f.size === file.size\r\n );\r\n \r\n // If not found, try with just the name for simpler matching\r\n if (!existingFile) {\r\n existingFile = window.globalState.mainView.availableFiles.find(\r\n f => f.name === file.name\r\n );\r\n }\r\n\r\n if (existingFile) {\r\n // Use the existing file\r\n console.log(`Using existing file: ${existingFile.name} (${existingFile.id})`);\r\n processedFile = existingFile;\r\n } else {\r\n // Upload new file\r\n console.log(`Uploading new file: ${file.name}`);\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n \r\n try {\r\n processedFile = await window.globalUtils.uploadFile('/api/files/upload', formData);\r\n console.log(`File uploaded successfully: ${processedFile.name} (${processedFile.id})`);\r\n \r\n // Add to global available files\r\n window.globalState.mainView.availableFiles.push(processedFile);\r\n } catch (uploadError) {\r\n console.error(\"Error uploading file:\", uploadError);\r\n WorkflowUtils.showError(\"Upload Error\", `Failed to upload file: ${uploadError.message}`);\r\n throw uploadError;\r\n }\r\n }\r\n\r\n // Always add to additionalFiles for user-visible state\r\n if (processedFile) {\r\n const fileExists = WorkflowCoordination.userInputState.additionalFiles.some(f => f.id === processedFile.id);\r\n \r\n if (!fileExists) {\r\n WorkflowCoordination.userInputState.additionalFiles.push(processedFile);\r\n WorkflowUI.renderAdditionalFiles();\r\n WorkflowUI.updatePromptVisualization();\r\n \r\n // Show success message\r\n if (typeof WorkflowUI.showToast === 'function') {\r\n WorkflowUI.showToast(\"Datei hinzugefügt\", `${processedFile.name} wurde hinzugefügt`, \"success\");\r\n }\r\n } else {\r\n console.log(`File ${processedFile.name} already in additionalFiles, not adding again`);\r\n \r\n // Show info message\r\n if (typeof WorkflowUI.showToast === 'function') {\r\n WorkflowUI.showToast(\"Information\", `${processedFile.name} bereits hinzugefügt`, \"info\");\r\n }\r\n }\r\n }\r\n\r\n return processedFile;\r\n } catch (error) {\r\n console.error(\"Fehler beim Hochladen/Hinzufügen der Datei:\", error);\r\n WorkflowUtils.showError(\"Fehler beim Verarbeiten der Datei\", error.message);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Berechnet die Größe eines JSON-Objekts in Bytes (ungefähr)\r\n * @param {Object} obj - Das JSON-Objekt\r\n * @returns {number} - Ungefähre Größe in Bytes\r\n */\r\nfunction estimateJsonSize(obj) {\r\n if (!obj) return 0;\r\n \r\n try {\r\n const jsonString = JSON.stringify(obj);\r\n return jsonString.length * 2; // Unicode-Zeichen sind bis zu 2 Bytes\r\n } catch (e) {\r\n console.warn(\"Fehler bei der Berechnung der JSON-Größe:\", e);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Ruft den aktuellen Status eines Workflows ab\r\n * @param {string} workflowId - ID des Workflows\r\n * @returns {Promise<Object>} - Workflow-Status\r\n */\r\nasync function getWorkflowStatus(workflowId) {\r\n try {\r\n const status = await window.globalUtils.getWorkflowStatus(workflowId);\r\n \r\n // Datenstatistik aktualisieren\r\n updateDataCounters(0, estimateJsonSize(status));\r\n \r\n return status;\r\n } catch (error) {\r\n console.error(\"Fehler beim Abrufen des Workflow-Status:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Verbesserte Polling-Funktion mit klarer Verantwortlichkeit\r\n * Updated for the refactored workflow architecture\r\n */\r\nasync function pollWorkflowStatus(workflowId) {\r\n if (!workflowId) {\r\n console.error(\"No workflow ID available for polling\");\r\n return;\r\n }\r\n\r\n // If workflow is no longer running or has been stopped, end polling immediately\r\n if (workflowState.isStoppingWorkflow) {\r\n console.log(\"Workflow polling ended because workflow was stopped\");\r\n return;\r\n }\r\n \r\n try {\r\n // Fetch status\r\n const status = await getWorkflowStatus(workflowId);\r\n \r\n if (status) {\r\n // Log the status for debugging (less verbose)\r\n console.log(`Polling workflow ${workflowId}, status: ${status.status}`);\r\n \r\n // Check for workflow completion\r\n const isWorkflowComplete = status.status === 'completed' || \r\n status.workflow_complete === true;\r\n \r\n // Always poll logs and messages whether complete or not\r\n await pollWorkflowLogs(workflowId);\r\n await pollWorkflowMessages(workflowId);\r\n \r\n // Check for specific status changes\r\n if (status.status === 'failed' || status.status === 'stopped') {\r\n // Handle failure or stopped state - DIRECT CALL TO FINALIZE\r\n WorkflowCoordination.finalizeWorkflow(workflowId, status.status);\r\n return;\r\n }\r\n \r\n // IMPORTANT: Direct check for completion status and immediate UI update\r\n if (isWorkflowComplete) {\r\n console.log(\"Workflow is complete, finalizing immediately\");\r\n workflowState.isRunning = false;\r\n workflowState.isWorkflowComplete = true;\r\n workflowState.finalPollingCycle = false;\r\n \r\n // IMPORTANT: Stop any waiting animations immediately\r\n if (typeof WorkflowCoordination.stopWaitingAnimation === 'function') {\r\n WorkflowCoordination.stopWaitingAnimation();\r\n }\r\n \r\n // Force UI update directly\r\n if (typeof WorkflowUI.updateWorkflowButtonsState === 'function') {\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n }\r\n \r\n // Re-render logs to update any animations\r\n if (typeof WorkflowUI.renderLogs === 'function' && workflowState.logs) {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n \r\n // Now call finalize\r\n WorkflowCoordination.finalizeWorkflow(workflowId, \"completed\");\r\n return; // Important: stop polling after finalizing\r\n }\r\n \r\n // Check the data_stats from status and update UI\r\n if (status.data_stats) {\r\n const receivedBytes = status.data_stats.total_bytes_received || 0;\r\n const sentBytes = status.data_stats.total_bytes_sent || 0;\r\n \r\n // Only update if values are different from zero\r\n if (receivedBytes > 0 || sentBytes > 0) {\r\n WorkflowUI.updateDataStatistics(sentBytes, receivedBytes);\r\n }\r\n }\r\n \r\n // Update progress if available\r\n if (status.progress !== undefined) {\r\n // Find the latest log for the current activity\r\n const latestAgentLog = workflowState.logs.find(log => \r\n log.agentName && log.type === 'info' && !log.message.includes('completed')\r\n );\r\n \r\n if (latestAgentLog) {\r\n latestAgentLog.progress = status.progress;\r\n // Re-render logs to show updated progress\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n }\r\n \r\n // Continue polling only if we should be running and workflow is not complete\r\n if (workflowState.isRunning && !workflowState.isWorkflowComplete) {\r\n // Schedule the next poll\r\n setTimeout(() => pollWorkflowStatus(workflowId), 2000);\r\n } else {\r\n console.log(\"Ending polling as workflow is complete.\");\r\n\r\n // ADD THESE DIRECT FIXES RIGHT HERE\r\n console.log(\"Forcing UI update for completed workflow\");\r\n // 1. Directly update workflow state\r\n workflowState.isRunning = false;\r\n workflowState.isWorkflowComplete = true;\r\n // 2. Force stop any waiting animations\r\n if (WorkflowCoordination && typeof WorkflowCoordination.stopWaitingAnimation === 'function') {\r\n WorkflowCoordination.stopWaitingAnimation();\r\n } else {\r\n // Fallback if function not available\r\n if (window.waitingDotsInterval) {\r\n clearInterval(window.waitingDotsInterval);\r\n }\r\n // Force all logs to stop waiting\r\n if (workflowState && workflowState.logs) {\r\n workflowState.logs.forEach(log => log.waiting = false);\r\n }\r\n }\r\n // 3. Direct DOM manipulation as a last resort to hide the stop button\r\n try {\r\n const stopBtn = document.getElementById('stop-workflow-btn');\r\n if (stopBtn) {\r\n console.log(\"DIRECT FIX: Hiding stop button via direct DOM access\");\r\n stopBtn.style.display = 'none';\r\n }\r\n } catch (e) {\r\n console.error(\"Error hiding stop button:\", e);\r\n }\r\n // 4. Force re-render logs\r\n if (WorkflowUI && typeof WorkflowUI.renderLogs === 'function') {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n // 5. Force update button states\r\n if (WorkflowUI && typeof WorkflowUI.updateWorkflowButtonsState === 'function') {\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n }\r\n }\r\n } else {\r\n WorkflowCoordination.addLogEntry(\"Keine Status-Informationen verfügbar\", \"warning\");\r\n \r\n // If we couldn't get status, try again after a short delay unless stopping\r\n if (workflowState.isRunning && !workflowState.isWorkflowComplete) {\r\n setTimeout(() => pollWorkflowStatus(workflowId), 3000); // Longer delay on error\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error fetching workflow status:\", error);\r\n WorkflowCoordination.addLogEntry(`Fehler beim Abrufen des Workflow-Status: ${error.message}`, \"error\");\r\n \r\n // On error, make one more attempt after a delay unless stopping\r\n if (workflowState.isRunning && !workflowState.isWorkflowComplete) {\r\n setTimeout(() => pollWorkflowStatus(workflowId), 5000); // Even longer delay after error\r\n } else {\r\n WorkflowCoordination.finishWorkflow(); // End the workflow on persistent errors\r\n }\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Löscht eine Nachricht aus dem Workflow\r\n * Updated for better error handling\r\n */\r\nasync function deleteWorkflowMessage(workflowId, messageId) {\r\n if (!workflowId || !messageId) {\r\n console.error(\"Ungültige Parameter für deleteWorkflowMessage\");\r\n return false;\r\n }\r\n \r\n try {\r\n // Endpunkt-URL korrekt formatieren\r\n const endpointUrl = `/api/workflows/${workflowId}/messages/${messageId}`;\r\n console.log(`Sende Löschantrag an: ${endpointUrl}`);\r\n \r\n // API-Aufruf zum Löschen der Nachricht\r\n const response = await window.globalUtils.deleteApiData(endpointUrl);\r\n \r\n // Antwort protokollieren für Debugging\r\n console.log(\"Lösch-Nachricht-Antwort:\", response);\r\n \r\n // Erfolg basierend auf Antwort zurückgeben\r\n return response && (response.success || response.status === 200 || response.status === \"success\");\r\n } catch (error) {\r\n console.error(\"Fehler beim Löschen der Nachricht:\", error);\r\n \r\n // Try to be forgiving with 404 errors - the message might already be gone\r\n if (error.message && error.message.includes(\"404\")) {\r\n console.log(\"Message not found (404), considering it deleted\");\r\n return true;\r\n }\r\n \r\n return false;\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Löscht eine Datei aus einer Workflow-Nachricht\r\n * Updated with improved error handling and ID matching\r\n */\r\nasync function deleteFileFromMessage(workflowId, messageId, fileId) {\r\n try {\r\n // Log detailed information for debugging\r\n console.log(`Deleting file from message: workflow=${workflowId}, message=${messageId}, file=${fileId}`);\r\n \r\n // Endpunkt-URL korrekt formatieren\r\n const endpointUrl = `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`;\r\n console.log(`Sende Löschantrag an: ${endpointUrl}`);\r\n \r\n try {\r\n // API-Aufruf\r\n const response = await window.globalUtils.deleteApiData(endpointUrl);\r\n \r\n // Antwort protokollieren für Debugging\r\n console.log(\"Dateilöschungs-Antwort:\", response);\r\n \r\n // Erfolg basierend auf Antwort zurückgeben\r\n return response && (response.success === true || response.status === 200 || response.status === \"success\");\r\n } catch (apiError) {\r\n // Fehlermeldung loggen\r\n console.error(\"Fehler beim Löschen der Datei aus der Nachricht:\", apiError);\r\n \r\n // Wenn die Datei nicht gefunden wurde, trotzdem Erfolg melden,\r\n // damit die UI-Aktualisierung erfolgt\r\n if (apiError.message && (apiError.message.includes(\"404\") || \r\n apiError.message.includes(\"not found\"))) {\r\n console.log(\"Datei wurde nicht gefunden, entferne aus UI\");\r\n return true;\r\n }\r\n \r\n // Handle mismatched IDs - try with partial matching if exact ID failed\r\n if (fileId && fileId.length > 10 && (apiError.message && apiError.message.includes(\"404\"))) {\r\n // Try again with shorter file ID (first 8 chars) for better matching with backend\r\n const shorterId = fileId.substring(0, 8);\r\n console.log(`Retry with shorter ID: ${shorterId}`);\r\n \r\n try {\r\n const retryEndpoint = `/api/workflows/${workflowId}/messages/${messageId}/files/${shorterId}`;\r\n const retryResponse = await window.globalUtils.deleteApiData(retryEndpoint);\r\n console.log(\"Retry deletion response:\", retryResponse);\r\n return true;\r\n } catch (retryError) {\r\n console.log(\"Retry also failed:\", retryError);\r\n // Silently succeed anyway to ensure UI cleanup\r\n return true;\r\n }\r\n }\r\n \r\n return false;\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Löschen der Datei aus der Nachricht:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Ensures all messages are loaded for a workflow\r\n * Updated for compatibility with refactored backend\r\n * @param {string} workflowId - ID of the workflow\r\n */\r\nasync function ensureAllMessagesLoaded(workflowId) {\r\n try {\r\n console.log(`Ensuring all messages are loaded for workflow ${workflowId}`);\r\n const messages = await window.globalUtils.getWorkflowMessages(workflowId);\r\n \r\n // Update data counters\r\n updateDataCounters(0, estimateJsonSize(messages));\r\n \r\n if (!messages || !Array.isArray(messages) || messages.length === 0) {\r\n return [];\r\n }\r\n \r\n // Process messages if workflowState exists\r\n if (workflowState) {\r\n const existingMessageIds = new Set(workflowState.chatMessages.map(msg => msg.id));\r\n \r\n // Add any missing messages\r\n let hasNewMessages = false;\r\n \r\n messages.forEach(message => {\r\n if (!existingMessageIds.has(message.id)) {\r\n const newMessage = {\r\n id: message.id,\r\n agent_id: message.agent_id || message.agent_type,\r\n agent_name: message.agent_name || message.agent_type || 'Agent',\r\n content: message.content || '',\r\n type: message.role === 'user' ? 'user' : 'agent',\r\n timestamp: message.started_at || message.created_at || new Date().toISOString(),\r\n documents: message.documents || [],\r\n workflow_complete: message.workflow_complete || false\r\n };\r\n \r\n // Also check for workflow completion\r\n if (message.workflow_complete) {\r\n workflowState.isWorkflowComplete = true;\r\n }\r\n \r\n workflowState.chatMessages.push(newMessage);\r\n workflowState.lastPolledMessageId = message.id;\r\n hasNewMessages = true;\r\n }\r\n });\r\n \r\n if (hasNewMessages) {\r\n // Sort messages by sequence or timestamp if needed\r\n workflowState.chatMessages.sort((a, b) => {\r\n const seqA = parseInt(a.sequence_no || 0);\r\n const seqB = parseInt(b.sequence_no || 0);\r\n \r\n if (seqA !== seqB) return seqA - seqB;\r\n \r\n // Fall back to timestamp\r\n const timeA = new Date(a.timestamp || 0).getTime();\r\n const timeB = new Date(b.timestamp || 0).getTime();\r\n return timeA - timeB;\r\n });\r\n \r\n // Render messages\r\n try {\r\n WorkflowUI.renderChatMessages(workflowState.chatMessages, workflowId);\r\n } catch (renderError) {\r\n console.error(\"Error rendering chat messages:\", renderError);\r\n }\r\n }\r\n }\r\n \r\n return messages;\r\n } catch (error) {\r\n console.error(\"Error ensuring all messages are loaded:\", error);\r\n return [];\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Ruft die Logs eines Workflows ab\r\n * Updated for the new workflow architecture\r\n * @param {string} workflowId - ID des Workflows\r\n */\r\nasync function pollWorkflowLogs(workflowId) {\r\n try {\r\n const logs = await window.globalUtils.getWorkflowLogs(workflowId);\r\n \r\n // Datenstatistik aktualisieren\r\n updateDataCounters(0, estimateJsonSize(logs));\r\n \r\n if (!logs || !Array.isArray(logs) || logs.length === 0) {\r\n return;\r\n }\r\n\r\n let hasNewLogs = false;\r\n const existingLogIds = new Set(workflowState.logs.map(log => log.id));\r\n \r\n // Nur neue Logs verarbeiten\r\n logs.forEach(log => {\r\n // Überprüfen, ob dieser Log bereits existiert\r\n if (!existingLogIds.has(log.id)) {\r\n // Agent information extraction - adapted to handle new format\r\n let agentName = null;\r\n \r\n // Handle both old and new agent information formats\r\n if (log.agent_name) {\r\n agentName = log.agent_name;\r\n } else if (log.agent && log.agent.name) {\r\n agentName = log.agent.name;\r\n }\r\n \r\n // Prüfen, ob detaillierte Inhalte vorhanden sind\r\n let details = null;\r\n \r\n // Detailierte Textinhalte extrahieren, falls vorhanden\r\n if (log.details && typeof log.details === 'string' && log.details.length > 0) {\r\n details = log.details;\r\n } else if (log.content && typeof log.content === 'string' && log.content.length > 0) {\r\n details = log.content;\r\n } else if (log.message && log.message.length > 100) {\r\n // For very long messages, treat the message itself as details\r\n details = log.message;\r\n }\r\n \r\n // Extract progress information if available\r\n let progress = undefined;\r\n if (log.progress !== undefined) {\r\n progress = log.progress;\r\n } else if (log.metadata && log.metadata.progress !== undefined) {\r\n progress = log.metadata.progress;\r\n }\r\n \r\n // Spezielle Logs für Ergebnisse\r\n if (log.result && log.agent_name) {\r\n WorkflowCoordination.addLogEntry(\r\n `Assistent ${log.agent_name} hat ein Ergebnis erstellt`, \r\n \"success\", \r\n log.result.content || \"Kein Inhalt verfügbar\", \r\n log.agent_name\r\n );\r\n } else {\r\n // Normales Log hinzufügen mit eventuellem Detail\r\n workflowState.logs.push({\r\n id: log.id,\r\n message: log.message,\r\n type: log.type || log.level || 'info',\r\n details: details,\r\n agentName: agentName,\r\n timestamp: log.timestamp || new Date().toISOString(),\r\n progress: progress // Add progress information\r\n });\r\n }\r\n \r\n // Letzte Log-ID für nächstes Polling speichern\r\n workflowState.lastPolledLogId = log.id;\r\n hasNewLogs = true;\r\n }\r\n });\r\n \r\n if (hasNewLogs) {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n \r\n // Find the latest active log for animation\r\n const latestLogs = workflowState.logs\r\n .filter(log => log.agentName && log.type === 'info' && !log.message.includes('completed'))\r\n .sort((a, b) => {\r\n const timeA = new Date(a.timestamp).getTime();\r\n const timeB = new Date(b.timestamp).getTime();\r\n return timeB - timeA; // Latest first\r\n });\r\n \r\n if (latestLogs.length > 0) {\r\n // Start waiting animation on the most recent active log\r\n WorkflowCoordination.startWaitingAnimation(latestLogs[0].id);\r\n } else {\r\n // If no active logs found, stop any waiting animation\r\n WorkflowCoordination.stopWaitingAnimation();\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Abrufen der Workflow-Logs:\", error);\r\n }\r\n}\r\n\r\n/**\r\n * Ruft die Nachrichten eines Workflows ab\r\n * Updated to handle the new document structure\r\n * @param {string} workflowId - ID des Workflows\r\n */\r\nasync function pollWorkflowMessages(workflowId) {\r\n try {\r\n // Debug logging\r\n console.log(`Polling messages for workflow ${workflowId}`);\r\n const messages = await window.globalUtils.getWorkflowMessages(workflowId);\r\n console.log(`Received ${messages ? messages.length : 0} messages from API`);\r\n \r\n // Datenstatistik aktualisieren\r\n updateDataCounters(0, estimateJsonSize(messages));\r\n \r\n if (!messages || !Array.isArray(messages) || messages.length === 0) {\r\n return;\r\n }\r\n\r\n let hasNewMessages = false;\r\n const existingMessageIds = new Set(workflowState.chatMessages.map(msg => msg.id));\r\n \r\n // Jede Nachricht verarbeiten, aber nur neue hinzufügen\r\n messages.forEach(message => {\r\n // Nur neue Nachrichten hinzufügen\r\n if (!existingMessageIds.has(message.id)) {\r\n const newMessage = {\r\n id: message.id,\r\n agent_id: message.agent_id || message.agent_type,\r\n agent_name: message.agent_name || message.agent_type || 'Agent',\r\n content: message.content || '',\r\n type: message.role === 'user' ? 'user' : 'agent',\r\n timestamp: message.started_at || message.created_at || new Date().toISOString(),\r\n documents: message.documents || [],\r\n workflow_complete: message.workflow_complete || false\r\n };\r\n \r\n // If message indicates workflow completion, update the state\r\n if (message.workflow_complete === true) {\r\n workflowState.isWorkflowComplete = true;\r\n }\r\n \r\n workflowState.chatMessages.push(newMessage);\r\n workflowState.lastPolledMessageId = message.id;\r\n hasNewMessages = true;\r\n }\r\n });\r\n \r\n if (hasNewMessages) {\r\n WorkflowUI.renderChatMessages(workflowState.chatMessages, workflowId);\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Abrufen der Workflow-Nachrichten:\", error);\r\n }\r\n}\r\n\r\n/**\r\n * Ruft die Nachrichten eines Workflows ab\r\n * @param {string} workflowId - ID des Workflows\r\n * @returns {Promise<Array>} - Liste der Workflow-Nachrichten\r\n */\r\nasync function getWorkflowMessages(workflowId) {\r\n try {\r\n const messages = await window.globalUtils.getWorkflowMessages(workflowId);\r\n \r\n // Datenstatistik aktualisieren\r\n updateDataCounters(0, estimateJsonSize(messages));\r\n \r\n return messages;\r\n } catch (error) {\r\n console.error(\"Fehler beim Abrufen der Workflow-Nachrichten:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Erstellt einen neuen Workflow\r\n * Updated to match the new workflow creation API\r\n * @param {string} promptText - Text für den Workflow-Prompt\r\n * @param {Array} selectedFiles - Array mit ausgewählten Dateien\r\n * @param {string} workflowName - Optionaler Name für den Workflow\r\n * @returns {Promise<Object>} - Erstellter Workflow\r\n */\r\nasync function createWorkflow(promptText, selectedFiles, workflowName = null) {\r\n try {\r\n // Workflow-Daten vorbereiten - account for API changes\r\n const workflowData = {\r\n name: workflowName || `Workflow ${new Date().toLocaleString()}`,\r\n prompt: promptText,\r\n files: selectedFiles.map(file => file.id)\r\n };\r\n \r\n // Geschätzte Datengröße für den Prompt\r\n const estimatedPromptSize = promptText.length + estimateJsonSize(selectedFiles);\r\n updateDataCounters(estimatedPromptSize, 0);\r\n \r\n console.log(\"Creating new workflow with data:\", workflowData);\r\n \r\n // Workflow starten über API\r\n const response = await window.globalUtils.writeApiData('/api/workflows', workflowData);\r\n \r\n // Datenempfang für Antwort registrieren\r\n updateDataCounters(0, estimateJsonSize(response));\r\n \r\n // Log the complete response for debugging\r\n console.log(\"Workflow creation response:\", response);\r\n \r\n return response;\r\n } catch (error) {\r\n console.error(\"Fehler beim Erstellen des Workflows:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Sendet eine Benutzereingabe an einen laufenden Workflow\r\n * Updated for the refactored backend\r\n * @param {string} workflowId - ID des Workflows\r\n * @param {string} message - Benutzernachricht\r\n * @param {Array} additionalFileIds - IDs zusätzlicher Dateien\r\n * @returns {Promise<Object>} - API-Antwort\r\n */\r\nasync function submitUserInput(workflowId, message, additionalFileIds = []) {\r\n try {\r\n // Log detailed information for debugging\r\n console.log(`Submitting user input to workflow ${workflowId}`);\r\n console.log(`Message content: ${message.substring(0, 100)}${message.length > 100 ? '...' : ''}`);\r\n console.log(`Additional files: ${additionalFileIds.join(', ')}`);\r\n \r\n // Anforderungsdaten vorbereiten\r\n const requestData = {\r\n message: message,\r\n additional_files: additionalFileIds || []\r\n };\r\n \r\n // Geschätzte Datengröße\r\n const estimatedSize = message.length + estimateJsonSize(additionalFileIds);\r\n updateDataCounters(estimatedSize, 0);\r\n \r\n // API aufrufen\r\n const response = await window.globalUtils.submitUserInput(workflowId, message, additionalFileIds);\r\n \r\n // Datenempfang für Antwort registrieren\r\n updateDataCounters(0, estimateJsonSize(response));\r\n \r\n console.log(\"User input submission response:\", response);\r\n \r\n return response;\r\n } catch (error) {\r\n console.error(\"Fehler beim Senden der Benutzereingabe:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Stoppt einen laufenden Workflow\r\n * @param {string} workflowId - ID des Workflows\r\n * @returns {Promise<Object>} - API-Antwort\r\n */\r\nasync function stopWorkflow(workflowId) {\r\n try {\r\n // Kleine Anfrage, daher minimal Daten\r\n updateDataCounters(10, 0);\r\n \r\n console.log(`Stopping workflow ${workflowId}`);\r\n \r\n const response = await window.globalUtils.stopWorkflow(workflowId);\r\n \r\n // Datenempfang für Antwort registrieren\r\n updateDataCounters(0, estimateJsonSize(response));\r\n \r\n console.log(\"Workflow stop response:\", response);\r\n \r\n return response;\r\n } catch (error) {\r\n console.error(\"Fehler beim Stoppen des Workflows:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n// Exportiere Funktionen\r\nexport {\r\n initDataLayer,\r\n getWorkflowStatus,\r\n pollWorkflowLogs,\r\n pollWorkflowMessages,\r\n getWorkflowMessages,\r\n createWorkflow,\r\n submitUserInput,\r\n stopWorkflow,\r\n pollWorkflowStatus,\r\n uploadAndAddFile,\r\n ensureAllMessagesLoaded,\r\n deleteWorkflowMessage,\r\n deleteFileFromMessage\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_52ab0f55-4c96-4814-bb21-1cbee26d2dda",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_f3b75dd3-cd62-468d-88ff-095e9becef08",
|
|
"name": "workflow_utils.js",
|
|
"content_type": "application/javascript",
|
|
"size": 10933,
|
|
"upload_date": "2025-04-15T17:13:30.169888"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * workflow_utils.js\r\n * Hilfsfunktionen für das Workflow-Modul\r\n */\r\n\r\n/**\r\n * Zeigt einen Bestätigungsdialog an\r\n * @param {string} message - Angezeigte Nachricht\r\n * @param {Function} onConfirm - Callback-Funktion bei Bestätigung\r\n */\r\nfunction showConfirm(message, onConfirm) {\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showConfirm) {\r\n window.appUtils.ui.showConfirm(message, onConfirm);\r\n } else {\r\n if (confirm(message)) {\r\n onConfirm();\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt eine Fehlermeldung an\r\n * @param {string} title - Titel der Fehlermeldung\r\n * @param {string} message - Fehlernachricht\r\n */\r\nfunction showError(title, message) {\r\n if (window.appUtils && window.appUtils.ui) {\r\n window.appUtils.ui.showError(title, message);\r\n } else {\r\n alert(`${title}: ${message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Prüft, ob der Inhalt einer Datei extrahiert werden kann\r\n * @param {Object} file - Dateiobjekt\r\n * @returns {boolean} - true, wenn Datei extrahierbar ist\r\n */\r\nfunction isFileExtractable(file) {\r\n if (!file || !file.content_type) return false;\r\n \r\n // Liste der Dateitypen, aus denen Text extrahiert werden kann\r\n const extractableTypes = [\r\n 'text/plain', 'text/csv', 'application/json', 'text/html',\r\n 'application/pdf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\r\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\r\n ];\r\n \r\n return extractableTypes.some(type => \r\n file.content_type.toLowerCase().includes(type.toLowerCase())\r\n );\r\n}\r\n\r\n/**\r\n * Liefert Icon-Klasse basierend auf Dateityp\r\n * @param {Object} file - Dateiobjekt\r\n * @returns {string} - CSS-Klassenname für das Icon\r\n */\r\nfunction getFileTypeIcon(file) {\r\n if (!file || !file.content_type) return 'fa-file';\r\n \r\n const type = file.content_type.toLowerCase();\r\n \r\n if (type.includes('image')) return 'fa-file-image';\r\n if (type.includes('pdf')) return 'fa-file-pdf';\r\n if (type.includes('word') || type.includes('document')) return 'fa-file-word';\r\n if (type.includes('excel') || type.includes('spreadsheet') || type.includes('csv')) return 'fa-file-excel';\r\n if (type.includes('html')) return 'fa-file-code';\r\n if (type.includes('json')) return 'fa-file-code';\r\n if (type.includes('text')) return 'fa-file-alt';\r\n \r\n return 'fa-file';\r\n}\r\n\r\n/**\r\n * Verbesserte Markdown-ähnliche Formatierung für Log-Einträge und Chat-Nachrichten\r\n * @param {string} text - Zu formatierender Text\r\n * @returns {string} - Formatierter HTML-Text\r\n */\r\n/**\r\n * Verbesserte Markdown-ähnliche Formatierung für Log-Einträge und Chat-Nachrichten\r\n * @param {string} text - Zu formatierender Text\r\n * @returns {string} - Formatierter HTML-Text\r\n */\r\nfunction formatMarkdownLike(text) {\r\n if (!text) return '';\r\n \r\n // Hilfsfunktion zum sicheren Ersetzen von Regex-Mustern\r\n const safeReplace = (text, pattern, replacement, flags = 'g') => {\r\n try {\r\n return text.replace(new RegExp(pattern, flags), replacement);\r\n } catch (e) {\r\n console.warn(`Fehler beim Ersetzen von Pattern ${pattern}:`, e);\r\n return text;\r\n }\r\n };\r\n\r\n // HTML-Zeichen escapen, um XSS zu vermeiden\r\n // Aber wir müssen den Originaltext aufbewahren für spätere Ersetzungen\r\n const originalText = text;\r\n let escapedText = escapeHtml(text);\r\n\r\n // 1. Tabellen zuerst parsen\r\n const tableRegex = /^\\|(.+)\\|\\n\\|[-:\\s|]+\\|\\n((?:\\|.+\\|\\n)*)/m;\r\n const tableMatch = originalText.match(tableRegex);\r\n \r\n if (tableMatch) {\r\n try {\r\n // Tabelle extrahieren und parsen\r\n const headerLine = tableMatch[1];\r\n const headers = headerLine.split('|').map(h => h.trim()).filter(h => h);\r\n \r\n const rowsText = tableMatch[2];\r\n const rowLines = rowsText.split('\\n').filter(line => line.trim() !== '');\r\n const rows = rowLines.map(row => \r\n row.split('|').map(cell => cell.trim()).filter(cell => cell || cell === '')\r\n );\r\n \r\n // HTML-Tabelle generieren\r\n const table = document.createElement('table');\r\n table.className = 'log-table';\r\n \r\n // Header\r\n const thead = document.createElement('thead');\r\n const headerRow = document.createElement('tr');\r\n headers.forEach(header => {\r\n const th = document.createElement('th');\r\n th.textContent = header;\r\n headerRow.appendChild(th);\r\n });\r\n thead.appendChild(headerRow);\r\n table.appendChild(thead);\r\n \r\n // Body\r\n const tbody = document.createElement('tbody');\r\n rows.forEach(rowData => {\r\n const tr = document.createElement('tr');\r\n rowData.forEach(cellData => {\r\n const td = document.createElement('td');\r\n td.textContent = cellData;\r\n tr.appendChild(td);\r\n });\r\n tbody.appendChild(tr);\r\n });\r\n table.appendChild(tbody);\r\n \r\n // Ersetze die originale Tabelle im escapierten Text\r\n const tablePattern = escapeRegExp(tableMatch[0]);\r\n escapedText = escapedText.replace(new RegExp(tablePattern, 'm'), table.outerHTML);\r\n } catch (e) {\r\n console.warn(\"Fehler beim Parsen der Tabelle:\", e);\r\n }\r\n }\r\n\r\n // 2. Code-Blöcke (vor anderen Formatierungen)\r\n const codeBlockRegex = /```([\\s\\S]*?)```/g;\r\n const codeBlocks = [];\r\n let codeBlockIndex = 0;\r\n \r\n escapedText = escapedText.replace(codeBlockRegex, (match, content) => {\r\n const placeholder = `%%CODE_BLOCK_${codeBlockIndex}%%`;\r\n codeBlocks.push(`<pre class=\"code-block\"><code>${escapeHtml(content.trim())}</code></pre>`);\r\n codeBlockIndex++;\r\n return placeholder;\r\n });\r\n\r\n // 3. Überschriften - mit dem multiline flag 'm'\r\n escapedText = safeReplace(escapedText, '^### (.+)$', '<h3>$1</h3>', 'gm');\r\n escapedText = safeReplace(escapedText, '^## (.+)$', '<h2>$1</h2>', 'gm');\r\n escapedText = safeReplace(escapedText, '^# (.+)$', '<h1>$1</h1>', 'gm');\r\n\r\n // 4. Listen - mit dem multiline flag 'm'\r\n escapedText = safeReplace(escapedText, '^\\\\* (.+)$', '<li>$1</li>', 'gm');\r\n escapedText = safeReplace(escapedText, '^- (.+)$', '<li>$1</li>', 'gm');\r\n \r\n // Wrap consecutive list items in ul tags - mit zusätzlichem 's' flag für mehrere Zeilen\r\n escapedText = safeReplace(escapedText, '(<li>.*?<\\\\/li>\\\\n*)+', '<ul>$&</ul>', 'gs');\r\n\r\n // 5. Fettdruck und Kursiv werden nur im bereits escapten Text ersetzt\r\n // Dadurch werden HTML-Tags in diesen Bereichen nicht versehentlich erzeugt\r\n escapedText = safeReplace(escapedText, '\\\\*\\\\*(.+?)\\\\*\\\\*', '<strong>$1</strong>');\r\n escapedText = safeReplace(escapedText, '\\\\*(.+?)\\\\*', '<em>$1</em>');\r\n\r\n // 6. Inline-Code\r\n escapedText = safeReplace(escapedText, '`([^`]+?)`', '<code>$1</code>');\r\n\r\n // 7. Zeilenumbrüche\r\n escapedText = safeReplace(escapedText, '\\\\n', '<br>');\r\n\r\n // Code-Blöcke wieder einsetzen\r\n codeBlocks.forEach((block, index) => {\r\n escapedText = escapedText.replace(`%%CODE_BLOCK_${index}%%`, block);\r\n });\r\n\r\n return escapedText;\r\n}\r\n\r\n/**\r\n * Escape-Funktion für reguläre Ausdrücke\r\n * @param {string} string - Zu escapender String\r\n * @returns {string} - Escapeter String\r\n */\r\nfunction escapeRegExp(string) {\r\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n\r\n/**\r\n * HTML-Zeichen escapen, um XSS zu verhindern\r\n * @param {string} text - Zu escapender Text\r\n * @returns {string} - Escapeter Text\r\n */\r\nfunction escapeHtml(text) {\r\n if (!text) return '';\r\n \r\n // Sicherere Methode ohne DOM-Manipulation\r\n return text\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n\r\n/**\r\n * Konvertiert Base64 in Blob\r\n * @param {string} base64 - Base64-kodierte Daten\r\n * @param {string} contentType - MIME-Typ des Inhalts\r\n * @returns {Blob} - Daten als Blob\r\n */\r\nfunction base64ToBlob(base64, contentType) {\r\n if (!base64) {\r\n console.warn(\"Leere Base64-Daten übergeben\");\r\n return new Blob([], { type: contentType || 'application/octet-stream' });\r\n }\r\n \r\n try {\r\n const byteCharacters = atob(base64);\r\n const byteArrays = [];\r\n \r\n for (let i = 0; i < byteCharacters.length; i += 512) {\r\n const slice = byteCharacters.slice(i, i + 512);\r\n \r\n const byteNumbers = new Array(slice.length);\r\n for (let j = 0; j < slice.length; j++) {\r\n byteNumbers[j] = slice.charCodeAt(j);\r\n }\r\n \r\n const byteArray = new Uint8Array(byteNumbers);\r\n byteArrays.push(byteArray);\r\n }\r\n \r\n return new Blob(byteArrays, { type: contentType || 'application/octet-stream' });\r\n } catch (error) {\r\n console.error(\"Fehler beim Konvertieren von Base64 zu Blob:\", error);\r\n return new Blob([], { type: contentType || 'application/octet-stream' });\r\n }\r\n}\r\n\r\n/**\r\n * Formatiert Dateigröße für die Anzeige\r\n * @param {number} bytes - Größe in Bytes\r\n * @returns {string} - Formatierte Größe\r\n */\r\nfunction formatFileSize(bytes) {\r\n if (bytes === undefined || bytes === null) return 'Unbekannt';\r\n \r\n if (bytes < 1024) return `${bytes} Bytes`;\r\n if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;\r\n if (bytes < 1073741824) return `${(bytes / 1048576).toFixed(1)} MB`;\r\n \r\n return `${(bytes / 1073741824).toFixed(1)} GB`;\r\n}\r\n\r\n/**\r\n * Extrahiert eine Moderator-Frage aus einer Nachricht\r\n * @param {Object} message - Die zu überprüfende Nachricht\r\n * @returns {string|null} - Die extrahierte Frage oder null\r\n */\r\nfunction extractModeratorQuestion(message) {\r\n if (!message || !message.content) return null;\r\n \r\n // Prüfen, ob es sich um eine Moderator-zu-User-Nachricht handelt\r\n if (message.content.includes('[Moderator zu User Agent]')) {\r\n return message.content.replace('[Moderator zu User Agent]', '').trim();\r\n }\r\n \r\n return null;\r\n}\r\n\r\n/**\r\n * Erstellt ein einzigartiges ID (für temporäre Elemente)\r\n * @returns {string} - Einzigartige ID\r\n */\r\nfunction generateUniqueId() {\r\n return `id_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;\r\n}\r\n\r\n\r\n// Alle Funktionen exportieren\r\nexport {\r\n showConfirm,\r\n showError,\r\n isFileExtractable,\r\n getFileTypeIcon,\r\n formatMarkdownLike,\r\n escapeHtml,\r\n base64ToBlob,\r\n formatFileSize,\r\n extractModeratorQuestion,\r\n generateUniqueId\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_50c482ba-b89a-49c6-9c7d-5ed3ef394798",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_8cbcf2e8-2029-4958-a7b3-7524d2f9021a",
|
|
"name": "workflow_coordination.js",
|
|
"content_type": "application/javascript",
|
|
"size": 24105,
|
|
"upload_date": "2025-04-15T17:13:30.169888"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * workflow_coordination.js\r\n * Zentrale Koordinationsschicht für UI und Feature-Interaktionen\r\n * Löst zirkuläre Abhängigkeiten zwischen workflow_features.js und workflow_ui.js\r\n */\r\n\r\n// Gemeinsam genutzter Workflow-Zustand\r\nlet workflowState = null;\r\n\r\n// Benutzeroberfläche-Zustand für Eingaben\r\nlet userInputState = {\r\n // Allgemeiner Zustand\r\n isInitialPrompt: true, // Flag für initialen Prompt vs. Antwort auf Agenten\r\n activeWorkflowId: null, // Aktuelle Workflow-ID (wenn im Kontext eines laufenden Workflows)\r\n isWaitingForResponse: false, // Flag für laufende API-Anfragen\r\n\r\n // Dateien\r\n additionalFiles: [], // Zusätzliche Dateien (während des Chats)\r\n \r\n // Eingabeelemente\r\n promptText: \"\", // Aktueller Prompt-Text oder Benutzerantwort\r\n \r\n // DOM-Element-Referenzen - Nur vorhandene DOM-Elemente referenzieren\r\n domElements: {\r\n // Für User-Agent-Antworten\r\n userInputArea: null,\r\n userMessageInput: null,\r\n sendUserMessageBtn: null,\r\n uploadAdditionalFileBtn: null,\r\n additionalFileInput: null,\r\n additionalFilesContainer: null,\r\n \r\n // Für Prompt-Auswahl\r\n promptSelectMain: null\r\n }\r\n};\r\n\r\n// Wartepunkte Animation\r\nlet waitingDotsInterval = null;\r\nlet lastWaitingLogId = null;\r\n\r\n// Module-Referenzen (werden bei Initialisierung gesetzt)\r\nlet WorkflowUI = null;\r\nlet WorkflowData = null;\r\n\r\n/**\r\n * Initialisiert das Koordinationsmodul\r\n * @param {Object} workflowStateObj - Gemeinsam genutzter Workflow-Zustand\r\n * @param {Object} modules - Referenzen auf andere Module\r\n */\r\nfunction initCoordination(workflowStateObj, modules) {\r\n console.log(\"Initialisiere Workflow-Koordination...\");\r\n \r\n workflowState = workflowStateObj;\r\n if (modules.ui) WorkflowUI = modules.ui;\r\n if (modules.data) WorkflowData = modules.data;\r\n console.log(\"Workflow-Koordination erfolgreich initialisiert\");\r\n}\r\n\r\n/**\r\n * Zeigt den Bereich für die User-Agent-Antwort an\r\n */\r\nfunction showInitialPromptView(previousResult = null) {\r\n const elements = userInputState.domElements;\r\n \r\n console.log(\"Show new prompt view\");\r\n \r\n userInputState.isInitialPrompt = true;\r\n \r\n // Platzhalter für initiale Prompts ändern\r\n if (elements.userMessageInput) {\r\n elements.userMessageInput.placeholder = \"Enter a new prompt...\";\r\n elements.userMessageInput.value = \"\"; // Clear any existing text\r\n userInputState.promptText = \"\";\r\n }\r\n \r\n // Button to \"Start\"\r\n if (elements.sendUserMessageBtn) {\r\n elements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-play\"></i> Start';\r\n }\r\n\r\n // Clear any attached files\r\n userInputState.additionalFiles = [];\r\n WorkflowUI.renderAdditionalFiles();\r\n \r\n // If previous result exists, show it\r\n if (previousResult && elements.userInputArea) {\r\n let promptElement = elements.userInputArea.querySelector('.user-prompt');\r\n if (!promptElement) {\r\n promptElement = document.createElement('div');\r\n promptElement.className = 'user-prompt';\r\n elements.userInputArea.insertBefore(promptElement, elements.userInputArea.firstChild);\r\n }\r\n \r\n promptElement.innerHTML = `\r\n <div class=\"previous-result\">\r\n <strong>Previous result:</strong> \r\n <span>${previousResult}</span>\r\n </div>\r\n `;\r\n } else if (elements.userInputArea) {\r\n // Remove any prompt element\r\n const promptElement = elements.userInputArea.querySelector('.user-prompt');\r\n if (promptElement) {\r\n promptElement.remove();\r\n }\r\n }\r\n \r\n // Focus input field\r\n if (elements.userMessageInput) {\r\n elements.userMessageInput.focus();\r\n }\r\n}\r\n\r\n/**\r\n * Blendet Benutzereingabebereiche aus\r\n */\r\nfunction hideUserInputAreas() {\r\n const elements = userInputState.domElements;\r\n \r\n // Statt Ausblenden, einfach den Eingabebereich zurücksetzen\r\n if (elements.userMessageInput) {\r\n elements.userMessageInput.value = \"\";\r\n userInputState.promptText = \"\";\r\n elements.userMessageInput.classList.remove('awaiting-input');\r\n }\r\n \r\n // Alle Moderator-Fragen entfernen\r\n if (elements.userInputArea) {\r\n const promptElement = elements.userInputArea.querySelector('.user-prompt');\r\n if (promptElement) {\r\n promptElement.remove();\r\n }\r\n }\r\n \r\n // Zusätzliche Dateien zurücksetzen\r\n userInputState.additionalFiles = [];\r\n WorkflowUI.renderAdditionalFiles();\r\n}\r\n\r\n/**\r\n * Setzt das UI in den Lade-Zustand\r\n * @param {boolean} isLoading - Ob das UI gesperrt werden soll\r\n */\r\nfunction setLoadingState(isLoading) {\r\n userInputState.isWaitingForResponse = isLoading;\r\n \r\n const elements = userInputState.domElements;\r\n \r\n console.log(`UI-Lade-Zustand: ${isLoading ? 'Aktiv' : 'Inaktiv'}`);\r\n \r\n // Eingabefeld für User-Agent-Antwort\r\n if (elements.userMessageInput) {\r\n elements.userMessageInput.disabled = isLoading;\r\n }\r\n \r\n // Buttons im User-Agent-Antwort-Bereich\r\n if (elements.sendUserMessageBtn) {\r\n elements.sendUserMessageBtn.disabled = isLoading;\r\n if (isLoading) {\r\n elements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-spinner fa-spin\"></i>';\r\n } else {\r\n // Unterschiedliche Icons basierend auf Kontext\r\n if (userInputState.isInitialPrompt) {\r\n elements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-play\"></i>Start';\r\n } else {\r\n elements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-paper-plane\"></i>Senden';\r\n }\r\n }\r\n }\r\n \r\n if (elements.uploadAdditionalFileBtn) {\r\n elements.uploadAdditionalFileBtn.disabled = isLoading;\r\n }\r\n \r\n // Andere UI-Elemente entsprechend dem Lade-Zustand aktualisieren\r\n if (elements.promptSelectMain) {\r\n elements.promptSelectMain.disabled = isLoading;\r\n }\r\n}\r\n\r\n/**\r\n * Punkte-Animation für wartende Logs starten\r\n */\r\nfunction startWaitingAnimation(logId) {\r\n // Falls bereits eine Animation läuft, diese stoppen\r\n stopWaitingAnimation();\r\n \r\n // Neues Log-Element als \"wartend\" markieren\r\n const log = workflowState.logs.find(l => l.id === logId);\r\n if (log) {\r\n // First remove waiting status from all logs\r\n workflowState.logs.forEach(l => l.waiting = false);\r\n \r\n // Then set it only for this log\r\n log.waiting = true;\r\n lastWaitingLogId = logId;\r\n \r\n // Animation starten\r\n waitingDotsInterval = setInterval(() => {\r\n updateWaitingDots();\r\n }, 500);\r\n \r\n // Log-Element neu rendern\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n}\r\n\r\n/**\r\n * Punkte-Animation für wartende Logs stoppen\r\n */\r\nfunction stopWaitingAnimation() {\r\n if (waitingDotsInterval) {\r\n clearInterval(waitingDotsInterval);\r\n waitingDotsInterval = null;\r\n }\r\n \r\n // Wartestatus in den Logs zurücksetzen\r\n if (lastWaitingLogId) {\r\n const log = workflowState.logs.find(l => l.id === lastWaitingLogId);\r\n if (log) {\r\n log.waiting = false;\r\n }\r\n lastWaitingLogId = null;\r\n }\r\n \r\n // IMPORTANT: Ensure all logs have waiting=false\r\n if (workflowState && workflowState.logs) {\r\n workflowState.logs.forEach(log => {\r\n if (log.waiting) {\r\n log.waiting = false;\r\n }\r\n });\r\n }\r\n \r\n // Make sure all waiting dots elements are cleared in the DOM\r\n try {\r\n document.querySelectorAll('.waiting-dots').forEach(el => {\r\n el.textContent = '';\r\n });\r\n } catch (e) {\r\n console.error(\"Error clearing waiting dots:\", e);\r\n }\r\n \r\n // Force re-render logs\r\n if (WorkflowUI && typeof WorkflowUI.renderLogs === 'function' && workflowState.logs) {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Punkte-Animation für \"Warten auf Daten\" aktualisieren\r\n */\r\nfunction updateWaitingDots() {\r\n const waitingDotsElements = document.querySelectorAll('.waiting-dots');\r\n waitingDotsElements.forEach(element => {\r\n // Aktuelle Punkte-Anzahl ermitteln\r\n const currentText = element.textContent || '';\r\n let dotsCount = currentText.length;\r\n \r\n // Anzahl erhöhen und begrenzen\r\n dotsCount = (dotsCount + 1) % 4;\r\n element.textContent = '.'.repeat(dotsCount);\r\n });\r\n}\r\n\r\n\r\n/**\r\n * Setzt den aktiven Workflow\r\n * @param {string} workflowId - ID des aktiven Workflows\r\n */\r\nfunction setActiveWorkflow(workflowId) {\r\n if (!workflowId) {\r\n console.error(\"Ungültige Workflow-ID\");\r\n return;\r\n }\r\n \r\n console.log(`Setze aktiven Workflow: ${workflowId}`);\r\n \r\n userInputState.activeWorkflowId = workflowId;\r\n \r\n // Auch den globalen Zustand aktualisieren für bessere Zugänglichkeit\r\n if (window.globalState && window.globalState.mainView) {\r\n window.globalState.mainView.currentWorkflowId = workflowId;\r\n console.log(\"Workflow-ID auch im globalState gesetzt\");\r\n }\r\n \r\n // Wenn ein Workflow aktiv ist, sind wir nicht mehr im initialen Prompt-Modus\r\n if (workflowId) {\r\n userInputState.isInitialPrompt = false;\r\n \r\n // UI-Status aktualisieren\r\n const elements = userInputState.domElements;\r\n if (elements.sendUserMessageBtn) {\r\n elements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-paper-plane\"></i> Senden';\r\n }\r\n \r\n // Stop-Button anzeigen\r\n if (elements.stopWorkflowBtn) {\r\n elements.stopWorkflowBtn.style.display = 'inline-block';\r\n }\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Finalisiert einen Workflow nach Abschluss\r\n * Enhanced for the refactored workflow architecture\r\n */\r\n/**\r\n * Finalisiert einen Workflow nach Abschluss\r\n * Enhanced for the refactored workflow architecture\r\n */\r\nasync function finalizeWorkflow(workflowId, status) {\r\n console.log(`Workflow ${workflowId} abgeschlossen mit Status: ${status}`);\r\n \r\n try {\r\n // Ensure all messages are loaded\r\n await WorkflowData.ensureAllMessagesLoaded(workflowId);\r\n \r\n // Update workflow state\r\n workflowState.isRunning = false;\r\n workflowState.isStoppingWorkflow = false;\r\n workflowState.isWorkflowComplete = true;\r\n workflowState.finalPollingCycle = false; // Ensure this is reset to prevent lingering polling\r\n \r\n // Stop any waiting animations\r\n stopWaitingAnimation();\r\n\r\n // Update UI - make sure this happens after state changes\r\n setTimeout(() => {\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n // Re-render logs to update any animations\r\n if (workflowState.logs) {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n }\r\n }, 10);\r\n\r\n // Add appropriate log\r\n if (status === 'completed') {\r\n addLogEntry(\"Workflow successfully completed\", \"success\");\r\n \r\n // Add system message to chat\r\n addChatMessage({\r\n type: 'system',\r\n content: 'Workflow completed, ready for new prompt',\r\n timestamp: new Date().toISOString()\r\n });\r\n \r\n // Check if there's a moderator asking user input message as the last message\r\n const lastMessage = workflowState.chatMessages[workflowState.chatMessages.length - 1];\r\n if (lastMessage && lastMessage.content && \r\n lastMessage.content.includes('[Moderator zu User Agent]')) {\r\n \r\n // Update to show we're waiting for user input\r\n addLogEntry(\"Waiting for user input to continue\", \"info\");\r\n \r\n // Make sure UI is ready for user input\r\n if (userInputState.userMessageInput) {\r\n userInputState.userMessageInput.focus();\r\n userInputState.userMessageInput.classList.add('awaiting-input');\r\n }\r\n } else {\r\n // Reset UI for new workflow input\r\n resetUserInput();\r\n showInitialPromptView();\r\n }\r\n } else if (status === 'failed') {\r\n addLogEntry(\"Workflow ended with errors\", \"error\");\r\n \r\n // Add system message to chat\r\n addChatMessage({\r\n type: 'system',\r\n content: 'Workflow ended with errors',\r\n timestamp: new Date().toISOString()\r\n });\r\n \r\n // Reset UI for new workflow input\r\n resetUserInput();\r\n showInitialPromptView();\r\n } else if (status === 'stopped') {\r\n addLogEntry(\"Workflow manually stopped\", \"warning\");\r\n \r\n // Add system message to chat\r\n addChatMessage({\r\n type: 'system',\r\n content: 'Workflow manually stopped',\r\n timestamp: new Date().toISOString()\r\n });\r\n \r\n // Reset UI for new workflow input\r\n resetUserInput();\r\n showInitialPromptView();\r\n }\r\n } catch (error) {\r\n console.error(\"Error in finalizeWorkflow:\", error);\r\n \r\n // Add error to logs\r\n addLogEntry(`Error finalizing workflow: ${error.message}`, \"error\");\r\n \r\n // Reset UI as a fallback\r\n resetUserInput();\r\n showInitialPromptView();\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Workflow-UI zurücksetzen\r\n */\r\nfunction finishWorkflow() {\r\n workflowState.isRunning = false;\r\n workflowState.isStoppingWorkflow = false;\r\n workflowState.waitingForUserInput = false;\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // Benutzereingabebereich zurücksetzen\r\n hideUserInputAreas();\r\n}\r\n\r\n/**\r\n * Ausführungsprotokoll aktualisieren\r\n * mit Assistenten-Name und Auto-Collapse für Details\r\n */\r\nfunction addLogEntry(message, type = 'info', details = null, agentName = null) {\r\n // Animation stoppen, wenn ein neuer Log-Eintrag hinzugefügt wird\r\n stopWaitingAnimation();\r\n \r\n if (!workflowState) {\r\n console.error(\"Workflow-Zustand nicht verfügbar, Log-Eintrag kann nicht hinzugefügt werden\");\r\n return null;\r\n }\r\n \r\n console.log(`Füge Log-Eintrag hinzu: ${type} - ${message}${agentName ? ` [${agentName}]` : ''}`);\r\n \r\n const log = {\r\n id: `log_${Date.now()}`,\r\n message,\r\n type,\r\n details,\r\n agentName,\r\n timestamp: new Date().toISOString()\r\n };\r\n \r\n // Logs-Array erstellen, falls noch nicht vorhanden\r\n if (!workflowState.logs) {\r\n workflowState.logs = [];\r\n }\r\n \r\n // Spezielle Formatierung für bestimmte Nachrichtentypen\r\n if (message.includes(\"Agent\") && message.includes(\"ausgewählt\")) {\r\n log.type = \"info\";\r\n log.highlighted = true; // Spezielles Highlighting für wichtige Informationen\r\n }\r\n \r\n if (message.includes(\"Moderator\") && message.includes(\"analysiert\")) {\r\n log.type = \"info\";\r\n log.highlighted = true;\r\n }\r\n \r\n if (message.includes(\"abgeschlossen\") || message.includes(\"fertig\")) {\r\n log.type = \"success\";\r\n }\r\n \r\n workflowState.logs.push(log);\r\n \r\n // UI aktualisieren\r\n try {\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n } catch (error) {\r\n console.error(\"Fehler beim Rendern der Logs:\", error);\r\n }\r\n \r\n // Falls der Workflow läuft, nach dem Eintrag die Wartepunkte starten\r\n if (workflowState.isRunning) {\r\n startWaitingAnimation(log.id);\r\n }\r\n \r\n return log;\r\n}\r\n\r\n/**\r\n * Verbesserte addChatMessage-Funktion, die Moderator-Fragen verarbeitet\r\n * und Dateien aus verschiedenen Quellen korrekt zum Message-Objekt hinzufügt\r\n * Updated for new document structure\r\n */\r\nfunction addChatMessage(message) {\r\n // Sicherstellen, dass die Nachricht eine ID hat\r\n if (!message.id) {\r\n message.id = `msg_${message.type || 'unknown'}_${Date.now()}`;\r\n }\r\n \r\n if (!workflowState) {\r\n console.error(\"Workflow-Zustand nicht verfügbar, Chat-Nachricht kann nicht hinzugefügt werden\");\r\n return;\r\n }\r\n \r\n console.log(`Füge Chat-Nachricht hinzu (ID: ${message.id}, Typ: ${message.type || 'unbekannt'})`);\r\n \r\n // Sicherstellen, dass das documents-Array existiert\r\n if (!message.documents) {\r\n message.documents = [];\r\n }\r\n \r\n // Für Benutzernachrichten prüfen, ob wir Dateien aus userInputState hinzufügen müssen\r\n if (message.type === 'agent' && (message.agent_id === 'user_agent' || message.agent_name === 'User Agent')) {\r\n // Dateien zum Message-Objekt hinzufügen\r\n let filesToAdd = [...userInputState.additionalFiles];\r\n \r\n if (filesToAdd.length > 0) {\r\n console.log(`Füge ${filesToAdd.length} Dateien zum Message-Objekt hinzu`);\r\n \r\n // Dokument-Einträge für jede Datei erstellen\r\n filesToAdd.forEach(file => {\r\n // Prüfen, ob die Datei bereits in documents enthalten ist\r\n const fileExists = message.documents.some(doc => \r\n (doc.source && doc.source.id === file.id) ||\r\n (doc.id === file.id)\r\n );\r\n \r\n if (!fileExists) {\r\n // Create document with the new structure expected by the backend\r\n message.documents.push({\r\n id: `doc_${file.id}_${Date.now()}`,\r\n source: {\r\n type: 'file',\r\n id: file.id,\r\n name: file.name,\r\n content_type: file.content_type,\r\n size: file.size\r\n },\r\n contents: [{\r\n type: 'text',\r\n text: `File: ${file.name} (Type: ${file.content_type || 'unknown'})`\r\n }]\r\n });\r\n \r\n console.log(`Datei \"${file.name}\" zum Message-Objekt hinzugefügt`);\r\n }\r\n });\r\n }\r\n }\r\n \r\n // Prüfen auf Duplikate vermeiden\r\n if (!workflowState.chatMessages) {\r\n workflowState.chatMessages = [];\r\n }\r\n \r\n const messageExists = workflowState.chatMessages.some(m => m.id === message.id);\r\n if (!messageExists) {\r\n workflowState.chatMessages.push(message);\r\n console.log(`Nachricht mit ID ${message.id} zum Workflow hinzugefügt`);\r\n } else {\r\n console.log(`Nachricht mit ID ${message.id} existiert bereits, wird aktualisiert`);\r\n // Bestehende Nachricht aktualisieren\r\n const index = workflowState.chatMessages.findIndex(m => m.id === message.id);\r\n if (index !== -1) {\r\n workflowState.chatMessages[index] = message;\r\n }\r\n }\r\n \r\n // Spezielle Behandlung für Moderator-Nachrichten\r\n if (message.agent_name === 'Moderator' || \r\n (message.content && message.content.includes('[Moderator zu User Agent]'))) {\r\n \r\n // Moderator-Nachrichten loggen\r\n const formattedContent = message.content.replace('[Moderator zu User Agent]', '').trim();\r\n addLogEntry(\r\n `Moderator fragt den Benutzer: ${formattedContent.substring(0, 50)}${formattedContent.length > 50 ? '...' : ''}`, \r\n \"info\", \r\n null, \r\n \"Moderator\"\r\n );\r\n \r\n // Mark as workflow complete\r\n message.workflow_complete = true;\r\n \r\n // Workflow-Status aktualisieren\r\n if (workflowState.isRunning) {\r\n workflowState.isWorkflowComplete = true;\r\n \r\n // Wartepunkte-Animation stoppen\r\n stopWaitingAnimation();\r\n \r\n // UI-Status aktualisieren\r\n if (WorkflowUI && typeof WorkflowUI.updateWorkflowButtonsState === 'function') {\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n }\r\n }\r\n }\r\n \r\n // Check for summary agent which indicates workflow completion\r\n if (message.agent_type === 'summary' || message.agent_id === 'workflow_summary' || message.workflow_complete === true) {\r\n // Mark as workflow complete\r\n message.workflow_complete = true;\r\n \r\n // Update workflow state\r\n workflowState.isWorkflowComplete = true;\r\n \r\n // Log completion\r\n addLogEntry(\"Workflow complete, summary generated\", \"success\");\r\n \r\n // Update UI state\r\n if (WorkflowUI && typeof WorkflowUI.updateWorkflowButtonsState === 'function') {\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n }\r\n }\r\n \r\n // UI aktualisieren - mit Fehlerbehandlung\r\n try {\r\n WorkflowUI.renderChatMessages(workflowState.chatMessages, userInputState.activeWorkflowId);\r\n } catch (error) {\r\n console.error(\"Fehler beim Rendern der Chat-Nachrichten:\", error);\r\n \r\n // Manueller Fallback-Renderer falls der reguläre fehlschlägt\r\n const chatContainer = document.getElementById('agent-chat-messages');\r\n const emptyState = document.getElementById('empty-chat-state');\r\n \r\n if (chatContainer && workflowState.chatMessages && workflowState.chatMessages.length > 0) {\r\n if (emptyState) emptyState.style.display = 'none';\r\n chatContainer.style.display = 'block';\r\n \r\n // Einfacher Fallback-Renderer\r\n chatContainer.innerHTML = workflowState.chatMessages.map(msg => `\r\n <div class=\"chat-message ${msg.type === 'system' ? 'system-message' : 'agent-message'}\">\r\n <div class=\"message-header\">\r\n <span class=\"agent-name\">${msg.agent_name || msg.type || 'Unbekannt'}</span>\r\n </div>\r\n <div class=\"message-content\">${msg.content || ''}</div>\r\n </div>\r\n `).join('');\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Clears all attached files and updates the UI\r\n */\r\nfunction clearAttachedFiles() {\r\n userInputState.additionalFiles = [];\r\n WorkflowUI.renderAdditionalFiles();\r\n WorkflowUI.updatePromptVisualization();\r\n\r\n const fileContainer = document.getElementById('additional-files-container');\r\n if (fileContainer) {\r\n fileContainer.innerHTML = '';\r\n }\r\n \r\n // Also clear the file selection if it exists\r\n const fileInput = document.getElementById('additional-file-input');\r\n if (fileInput) {\r\n fileInput.value = '';\r\n }\r\n \r\n console.log(\"Files cleared from input area\");\r\n}\r\n\r\n/**\r\n * Setzt den Modul-Zustand zurück\r\n */\r\nfunction resetUserInput() {\r\n userInputState.isInitialPrompt = true;\r\n userInputState.activeWorkflowId = null;\r\n userInputState.isWaitingForResponse = false;\r\n userInputState.additionalFiles = [];\r\n userInputState.promptText = \"\";\r\n \r\n const elements = userInputState.domElements;\r\n \r\n // Eingabefelder zurücksetzen\r\n if (elements.userMessageInput) {\r\n elements.userMessageInput.value = \"\";\r\n }\r\n \r\n // Listen rendern\r\n WorkflowUI.renderAdditionalFiles();\r\n \r\n // Bereiche ausblenden\r\n hideUserInputAreas();\r\n}\r\n\r\n// Exportiere Funktionen und Zustände\r\nexport {\r\n initCoordination,\r\n userInputState,\r\n workflowState,\r\n showInitialPromptView,\r\n hideUserInputAreas,\r\n setLoadingState,\r\n startWaitingAnimation,\r\n stopWaitingAnimation,\r\n updateWaitingDots,\r\n setActiveWorkflow,\r\n finalizeWorkflow,\r\n finishWorkflow,\r\n addLogEntry,\r\n addChatMessage,\r\n clearAttachedFiles,\r\n resetUserInput\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_1432a622-5b38-4ed9-91af-1ee9b60be591",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_89297896-348c-48d5-b231-a20aea8a77b1",
|
|
"name": "mandates.js",
|
|
"content_type": "application/javascript",
|
|
"size": 10991,
|
|
"upload_date": "2025-04-15T17:13:30.169888"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * mandates.js\r\n * Mandanten-Modul mit dem generischen Entitätsansatz\r\n * Angepasst an das überarbeitete Backend\r\n */\r\n\r\n/**\r\n * Initialisierungsfunktion für das Mandanten-Modul\r\n * @param {Object} globalState - Globaler Zustand der Anwendung\r\n */\r\nfunction initMandatesModule(globalState) {\r\n // Prüfen, ob der Benutzer die erforderlichen Rechte hat (nur SysAdmin)\r\n if (!globalState.user || !globalState.user.isSysAdmin) {\r\n // Mandanten-Sektion ausblenden oder Hinweis anzeigen\r\n const mandatesView = document.getElementById('mandates-view');\r\n if (mandatesView) {\r\n mandatesView.innerHTML = `\r\n <h2>Mandantenverwaltung</h2>\r\n <div class=\"card\">\r\n <div class=\"alert alert-warning\">\r\n <i class=\"fas fa-exclamation-triangle\"></i>\r\n Sie benötigen SysAdmin-Rechte, um Mandanten zu verwalten.\r\n </div>\r\n </div>\r\n `;\r\n }\r\n return;\r\n }\r\n\r\n // Generisches Entitätsmodul für Mandanten initialisieren\r\n window.genericEntityModule.init(globalState, {\r\n entityType: 'mandate',\r\n apiEndpoint: '/api/mandates', // Backend-Endpunkt prüfen\r\n listContainerId: 'mandates-list-container',\r\n addButtonId: 'add-mandate-btn',\r\n \r\n // Benutzerdefinierte Render-Funktion für Mandanten\r\n renderItem: function(mandate) {\r\n // Status-Badge (aktiv/deaktiviert)\r\n const statusBadge = mandate.disabled ? \r\n '<span class=\"badge badge-red\">Deaktiviert</span>' : \r\n '<span class=\"badge badge-green\">Aktiv</span>';\r\n\r\n return `\r\n <div class=\"list-header\">\r\n <div>\r\n <h4>${mandate.name || 'Unbenannter Mandant'}</h4>\r\n </div>\r\n <div class=\"mandate-badges\">\r\n ${statusBadge}\r\n <span class=\"badge badge-blue\">Sprache: ${mandate.language || 'de'}</span>\r\n </div>\r\n </div>\r\n <div class=\"list-body\">\r\n ${mandate.description ? `<div class=\"mandate-description\">${mandate.description}</div>` : ''}\r\n ${mandate.contact_email ? `<div class=\"mandate-email\">${mandate.contact_email}</div>` : ''}\r\n ${mandate.created_at ? `Erstellt am: ${new Date(mandate.created_at).toLocaleString()}` : ''}\r\n </div>\r\n `;\r\n },\r\n \r\n // Behandlung spezieller Attributformate\r\n transformFormData: function(formData) {\r\n // Boolean-Wert für \"disabled\" korrekt umwandeln\r\n if (formData.hasOwnProperty('disabled') && typeof formData.disabled === 'string') {\r\n formData.disabled = formData.disabled.toLowerCase() === 'true';\r\n }\r\n \r\n return formData;\r\n },\r\n \r\n // Zusätzliche Aktionen für Mandanten\r\n getItemActions: function(mandate) {\r\n const actions = [];\r\n \r\n // Benutzer anzeigen Aktion\r\n actions.push({\r\n text: 'Benutzer anzeigen',\r\n className: 'entity-view-btn',\r\n html: '<i class=\"fas fa-users\"></i>',\r\n handler: function(mandate) {\r\n showMandateUsers(mandate);\r\n }\r\n });\r\n \r\n // Mandant aktivieren/deaktivieren\r\n if (mandate.disabled) {\r\n actions.push({\r\n text: 'Aktivieren',\r\n className: 'entity-activate-btn',\r\n html: '<i class=\"fas fa-check-circle\"></i>',\r\n handler: function(mandate) {\r\n toggleMandateStatus(mandate, false);\r\n }\r\n });\r\n } else {\r\n actions.push({\r\n text: 'Deaktivieren',\r\n className: 'entity-deactivate-btn',\r\n html: '<i class=\"fas fa-ban\"></i>',\r\n handler: function(mandate) {\r\n toggleMandateStatus(mandate, true);\r\n }\r\n });\r\n }\r\n \r\n return actions;\r\n },\r\n \r\n // Callbacks\r\n onItemCreated: function(mandate) {\r\n showToast(`Mandant \"${mandate.name}\" erstellt`);\r\n },\r\n \r\n onItemUpdated: function(mandate) {\r\n showToast(`Mandant \"${mandate.name}\" aktualisiert`);\r\n },\r\n \r\n onItemDeleted: function(mandateId) {\r\n showToast(`Mandant gelöscht`);\r\n }\r\n });\r\n}\r\n \r\n/**\r\n * Mandant aktivieren/deaktivieren\r\n * @param {Object} mandate - Der Mandant\r\n * @param {boolean} disabled - Neuer Status (true = deaktiviert, false = aktiviert)\r\n */\r\nasync function toggleMandateStatus(mandate, disabled) {\r\n try {\r\n // Status ändern via API\r\n const endpoint = `/api/mandates/${mandate.id}`;\r\n const response = await window.globalUtils.writeApiData(endpoint, { disabled });\r\n \r\n if (response) {\r\n showToast(`Mandant \"${mandate.name}\" ${disabled ? 'deaktiviert' : 'aktiviert'}`);\r\n \r\n // Liste neu laden\r\n if (window.genericEntityModule) {\r\n window.genericEntityModule.loadAndRenderItems();\r\n }\r\n }\r\n } catch (error) {\r\n console.error(`Fehler beim Ändern des Mandantenstatus:`, error);\r\n showError(`Fehler beim Ändern des Mandantenstatus`, error.message);\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt alle Benutzer eines Mandanten an\r\n * @param {Object} mandate - Der Mandant\r\n */\r\nasync function showMandateUsers(mandate) {\r\n try {\r\n // Benutzer für den Mandanten abrufen - API-Pfad angepasst\r\n const users = await window.globalUtils.readApiData(`/api/users?mandate_id=${mandate.id}`);\r\n \r\n if (!users || users.length === 0) {\r\n showToast(`Keine Benutzer für Mandant \"${mandate.name}\" gefunden.`);\r\n return;\r\n }\r\n \r\n // Modal mit Benutzerliste erstellen\r\n let userListHTML = `\r\n <h3>Benutzer von Mandant \"${mandate.name}\"</h3>\r\n <div class=\"mandate-users-list\">\r\n <table class=\"users-table\">\r\n <thead>\r\n <tr>\r\n <th>Benutzername</th>\r\n <th>Name</th>\r\n <th>E-Mail</th>\r\n <th>Rolle</th>\r\n <th>Status</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n `;\r\n \r\n users.forEach(user => {\r\n // Status und Rolle ermitteln\r\n const status = user.disabled ? \r\n '<span class=\"badge badge-red\">Deaktiviert</span>' : \r\n '<span class=\"badge badge-green\">Aktiv</span>';\r\n \r\n let role = '<span class=\"badge badge-gray\">User</span>';\r\n if (user.privilege === 'admin') {\r\n role = '<span class=\"badge badge-blue\">Admin</span>';\r\n } else if (user.privilege === 'sysadmin') {\r\n role = '<span class=\"badge badge-purple\">SysAdmin</span>';\r\n }\r\n \r\n userListHTML += `\r\n <tr>\r\n <td>${user.username}</td>\r\n <td>${user.full_name || '-'}</td>\r\n <td>${user.email || '-'}</td>\r\n <td>${role}</td>\r\n <td>${status}</td>\r\n </tr>\r\n `;\r\n });\r\n \r\n userListHTML += `\r\n </tbody>\r\n </table>\r\n </div>\r\n `;\r\n \r\n // Modal anzeigen\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.createModal) {\r\n window.appUtils.ui.createModal({\r\n title: `Benutzer von Mandant \"${mandate.name}\"`,\r\n content: userListHTML,\r\n submitText: 'Schließen',\r\n cancelText: null, // Kein Abbrechen-Button\r\n onSubmit: () => {\r\n // Einfach schließen\r\n }\r\n });\r\n } else {\r\n // Fallback: Alert mit Benutzerzahl\r\n showToast(`Der Mandant \"${mandate.name}\" hat ${users.length} Benutzer.`);\r\n }\r\n } catch (error) {\r\n console.error(`Fehler beim Abrufen der Mandantenbenutzer:`, error);\r\n showError(`Fehler beim Abrufen der Benutzer`, error.message);\r\n }\r\n}\r\n\r\n/**\r\n * Erstellt einen neuen Mandanten mit Default-Werten\r\n * @param {Object} globalStateObj - Globaler Zustand der Anwendung\r\n * @returns {Promise<Object>} - Der erstellte Mandant\r\n */\r\nasync function createDefaultMandate(globalStateObj) {\r\n try {\r\n // Default-Mandant erstellen - Anpassung an neue API\r\n const defaultMandate = {\r\n name: \"Neuer Mandant\",\r\n description: \"Beschreibung des Mandanten\",\r\n language: \"de\",\r\n contact_email: \"\"\r\n };\r\n \r\n // An die API senden\r\n const newMandate = await window.globalUtils.writeApiData('/api/mandates', defaultMandate);\r\n \r\n // UI aktualisieren\r\n if (window.genericEntityModule) {\r\n window.genericEntityModule.loadAndRenderItems();\r\n }\r\n \r\n return newMandate;\r\n } catch (error) {\r\n console.error(\"Fehler beim Erstellen des Default-Mandanten:\", error);\r\n showError(\"Fehler beim Erstellen\", error.message);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt eine Toast-Nachricht an\r\n * @param {string} message - Anzuzeigende Nachricht\r\n */\r\nfunction showToast(message) {\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showToast) {\r\n window.appUtils.ui.showToast(message);\r\n } else if (window.globalUtils && window.globalUtils.showError) {\r\n window.globalUtils.showError(\"Info\", message);\r\n } else {\r\n console.log(message);\r\n }\r\n}\r\n\r\n/**\r\n * Zeigt eine Fehlermeldung an\r\n * @param {string} title - Titel der Fehlermeldung\r\n * @param {string} message - Fehlermeldung\r\n */\r\nfunction showError(title, message) {\r\n if (window.appUtils && window.appUtils.ui && window.appUtils.ui.showError) {\r\n window.appUtils.ui.showError(title, message);\r\n } else if (window.globalUtils && window.globalUtils.showError) {\r\n window.globalUtils.showError(title, message);\r\n } else {\r\n console.error(`${title}: ${message}`);\r\n }\r\n}\r\n\r\n// Mandates-Modul-Schnittstelle definieren und exportieren\r\nwindow.initMandatesModule = initMandatesModule;\r\n\r\n// Export für ES Module\r\nexport {\r\n initMandatesModule,\r\n toggleMandateStatus,\r\n showMandateUsers,\r\n createDefaultMandate\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_05ad9f3b-7fa2-4600-98ba-42a06328b178",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_bc64562c-bbf8-4503-b829-0c619001e9cd",
|
|
"name": "workflow_ui.js",
|
|
"content_type": "application/javascript",
|
|
"size": 78199,
|
|
"upload_date": "2025-04-15T17:13:30.170889"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * workflow_ui.js - REVISED AND COMPACTED\r\n * UI-Komponenten und Rendering-Funktionen für das Workflow-Modul\r\n * Angepasst für die neue dreispaltige Layout-Struktur\r\n */\r\n\r\nimport * as WorkflowUtils from './workflow_utils.js';\r\nimport * as WorkflowCoordination from './workflow_coordination.js';\r\n\r\n// DOM-Elemente\r\nlet domElements = {};\r\n\r\n// Referenz auf den Workflow-Zustand\r\nlet workflowState = null;\r\n\r\n// Callbacks\r\nlet callbacks = {\r\n onLayoutChange: null,\r\n onResetWorkflow: null,\r\n onStopWorkflow: null\r\n};\r\n\r\n// Status für Datenübertragung\r\nlet dataCounters = {\r\n sentDataSize: 0,\r\n receivedDataSize: 0\r\n};\r\n\r\n// Aktuelle Vorschaudatei\r\nlet currentPreviewFile = null;\r\n\r\n/**\r\n * Initialisiert die UI-Komponenten\r\n * @param {Object} workflowState - Gemeinsamer Workflow-Zustand\r\n * @param {Object} callbackFunctions - Callback-Funktionen für UI-Events\r\n */\r\nfunction initUI(workflowStateObj, callbackFunctions) {\r\n console.log(\"Initialisiere Workflow-UI...\");\r\n\r\n workflowState = workflowStateObj;\r\n\r\n // Callbacks speichern\r\n callbacks = {\r\n onLayoutChange: callbackFunctions.onLayoutChange || (() => {}),\r\n onResetWorkflow: callbackFunctions.onResetWorkflow || (() => {}),\r\n onStopWorkflow: callbackFunctions.onStopWorkflow || (() => {})\r\n };\r\n \r\n // DOM-Elemente referenzieren\r\n initDomElements();\r\n \r\n // Event-Listener einrichten\r\n setupEventListeners();\r\n \r\n // Initialen Zustand rendern\r\n renderLogs([]);\r\n renderChatMessages([], null);\r\n updateDataStatistics(0, 0);\r\n\r\n // Initialize button states properly\r\n updateWorkflowButtonsState(workflowStateObj);\r\n\r\n // Initialize fixed layout\r\n initializeFixedLayout();\r\n \r\n // Setup header toggle functionality\r\n setupHeaderToggle();\r\n\r\n // Update userInputState in coordination module with DOM elements\r\n WorkflowCoordination.userInputState.domElements = domElements;\r\n\r\n console.log(\"Workflow-UI erfolgreich initialisiert\");\r\n}\r\n\r\n/**\r\n * Initialisiert alle DOM-Elementreferenzen\r\n * Optimiert für die neue Layout-Struktur\r\n */\r\nfunction initDomElements() {\r\n domElements = {\r\n // Hauptcontainer - Überprüfen der korrekten IDs für das neue Layout\r\n resetBtn: document.getElementById('reset-btn'),\r\n stopWorkflowBtn: document.getElementById('stop-workflow-btn'),\r\n executionLog: document.getElementById('execution-log'),\r\n toggleHeaderBtn: document.getElementById('toggle-header-btn'),\r\n workflowHeader: document.querySelector('.workflow-header'),\r\n agentChatMessages: document.getElementById('agent-chat-messages'),\r\n emptyChatState: document.getElementById('empty-chat-state'),\r\n \r\n // Dateivorschau\r\n filePreviewContainer: document.getElementById('file-preview-container'),\r\n filePreviewContent: document.getElementById('file-preview-content'),\r\n downloadFileBtn: document.getElementById('download-file-btn'),\r\n copyFileBtn: document.getElementById('copy-file-btn'),\r\n \r\n // Statistik-Anzeige\r\n dataStatisticsEl: document.getElementById('data-statistics'),\r\n\r\n // Layout-Elemente für die neue Struktur\r\n headerLeft: document.querySelector('.header-left'),\r\n headerRight: document.querySelector('.header-right'),\r\n chatSection: document.querySelector('.chat-section'),\r\n chatLeft: document.querySelector('.chat-left'),\r\n chatRight: document.querySelector('.chat-right'),\r\n workflowFooter: document.querySelector('.workflow-footer'),\r\n footerLeft: document.querySelector('.footer-left'),\r\n footerRight: document.querySelector('.footer-right')\r\n };\r\n\r\n // Debug-Logging hinzufügen, um Probleme zu identifizieren\r\n console.log(\"DOM-Elemente initialisiert:\", \r\n Object.keys(domElements).filter(k => domElements[k] !== null).length + \" gefunden, \" + \r\n Object.keys(domElements).filter(k => domElements[k] === null).length + \" nicht gefunden\");\r\n \r\n // Optionales detailliertes Logging für fehlende Elemente\r\n const missingElements = Object.keys(domElements).filter(k => domElements[k] === null);\r\n if (missingElements.length > 0) {\r\n console.warn(\"Fehlende DOM-Elemente:\", missingElements.join(', '));\r\n }\r\n\r\n validateRequiredElements();\r\n}\r\n\r\n/**\r\n * Validiert, ob alle erforderlichen DOM-Elemente vorhanden sind\r\n */\r\nfunction validateRequiredElements() {\r\n const requiredElements = [\r\n 'executionLog',\r\n 'agentChatMessages',\r\n 'stopWorkflowBtn',\r\n 'resetBtn',\r\n 'workflowHeader',\r\n 'filePreviewContent'\r\n ];\r\n \r\n let allElementsExist = true;\r\n let missingElements = [];\r\n \r\n requiredElements.forEach(elementKey => {\r\n if (!domElements[elementKey]) {\r\n missingElements.push(elementKey);\r\n allElementsExist = false;\r\n }\r\n });\r\n \r\n if (missingElements.length > 0) {\r\n console.error(`Fehlende erforderliche DOM-Elemente: ${missingElements.join(', ')}`);\r\n \r\n // Versuche, die Elemente mit alternativen Selektoren zu finden\r\n missingElements.forEach(elementKey => {\r\n // Konvertiere camelCase zu kebab-case für ID-Suche\r\n const alternativeId = elementKey\r\n .replace(/([A-Z])/g, '-$1')\r\n .toLowerCase();\r\n \r\n console.log(`Versuche alternatives Element mit ID '${alternativeId}' zu finden`);\r\n const alternativeElement = document.getElementById(alternativeId);\r\n \r\n if (alternativeElement) {\r\n console.log(`Element mit alternativer ID '${alternativeId}' gefunden`);\r\n domElements[elementKey] = alternativeElement;\r\n }\r\n });\r\n }\r\n \r\n return allElementsExist;\r\n}\r\n\r\n/**\r\n * Richtet das Header-Toggle für das neue Layout ein\r\n */\r\nfunction setupHeaderToggle() {\r\n if (!domElements.toggleHeaderBtn || !domElements.workflowHeader) return;\r\n\r\n domElements.toggleHeaderBtn.addEventListener('click', function() {\r\n domElements.workflowHeader.classList.toggle('collapsed');\r\n \r\n // Icon aktualisieren\r\n const icon = domElements.toggleHeaderBtn.querySelector('i');\r\n if (icon) {\r\n if (domElements.workflowHeader.classList.contains('collapsed')) {\r\n icon.className = 'fas fa-plus';\r\n } else {\r\n icon.className = 'fas fa-minus';\r\n }\r\n }\r\n \r\n // Layout-Anpassung erzwingen\r\n adjustLayoutOnResize();\r\n });\r\n}\r\n\r\n/**\r\n * Event-Listener einrichten\r\n */\r\nfunction setupEventListeners() {\r\n // Workflow-Steuerung\r\n if (domElements.resetBtn) {\r\n domElements.resetBtn.addEventListener('click', () => {\r\n if (callbacks.onResetWorkflow) callbacks.onResetWorkflow();\r\n });\r\n } else {\r\n console.warn(\"Reset-Button nicht gefunden\");\r\n }\r\n\r\n if (domElements.stopWorkflowBtn) {\r\n domElements.stopWorkflowBtn.addEventListener('click', () => {\r\n if (callbacks.onStopWorkflow) callbacks.onStopWorkflow();\r\n });\r\n } else {\r\n console.warn(\"Stop-Workflow-Button nicht gefunden\");\r\n }\r\n \r\n // Dateivorschau-Steuerung\r\n if (domElements.downloadFileBtn) {\r\n domElements.downloadFileBtn.addEventListener('click', downloadPreviewFile);\r\n } else {\r\n console.warn(\"Download-File-Button nicht gefunden\");\r\n }\r\n \r\n if (domElements.copyFileBtn) {\r\n domElements.copyFileBtn.addEventListener('click', copyPreviewFileContent);\r\n } else {\r\n console.warn(\"Copy-File-Button nicht gefunden\");\r\n }\r\n \r\n // Header-Toggle für dreispaltiges Layout\r\n if (domElements.toggleHeaderBtn && domElements.workflowHeader) {\r\n domElements.toggleHeaderBtn.addEventListener('click', function() {\r\n domElements.workflowHeader.classList.toggle('collapsed');\r\n \r\n // Icon aktualisieren\r\n const icon = this.querySelector('i');\r\n if (icon) {\r\n if (domElements.workflowHeader.classList.contains('collapsed')) {\r\n icon.className = 'fas fa-plus';\r\n } else {\r\n icon.className = 'fas fa-minus';\r\n }\r\n }\r\n \r\n // Layout-Anpassung erzwingen\r\n adjustLayoutOnResize();\r\n });\r\n } else {\r\n console.warn(\"Header-Toggle-Funktionalität nicht verfügbar\");\r\n }\r\n \r\n // Fenstergröße-Event-Listener\r\n window.addEventListener('resize', adjustLayoutOnResize);\r\n \r\n console.log(\"Event-Listener eingerichtet\");\r\n}\r\n\r\n\r\n/**\r\n * Aktualisiert den Status aller Workflow-Buttons basierend auf dem aktuellen Zustand\r\n */\r\nfunction updateWorkflowButtonsState(workflowState) {\r\n console.log(\"Updating workflow buttons state:\", {\r\n isRunning: workflowState.isRunning,\r\n isComplete: workflowState.isWorkflowComplete,\r\n isStopping: workflowState.isStoppingWorkflow\r\n });\r\n \r\n if (!domElements.stopWorkflowBtn) return;\r\n \r\n // Update stop button\r\n if (workflowState.isRunning && !workflowState.isWorkflowComplete) {\r\n // Show stop button while workflow is running and not complete\r\n if (workflowState.isStoppingWorkflow) {\r\n domElements.stopWorkflowBtn.textContent = 'Stopping...';\r\n domElements.stopWorkflowBtn.disabled = true;\r\n } else {\r\n domElements.stopWorkflowBtn.innerHTML = '<i class=\"fas fa-stop\"></i> Stop';\r\n domElements.stopWorkflowBtn.disabled = false;\r\n }\r\n domElements.stopWorkflowBtn.style.display = 'inline-block';\r\n } else {\r\n // Hide stop button when not running or complete\r\n console.log(\"HIDING STOP BUTTON\");\r\n domElements.stopWorkflowBtn.style.display = 'none';\r\n }\r\n \r\n // Update reset button if available\r\n if (domElements.resetBtn) {\r\n domElements.resetBtn.disabled = workflowState.isStoppingWorkflow;\r\n }\r\n \r\n // Update send button and input field if they're part of domElements\r\n if (domElements.sendUserMessageBtn) {\r\n const canSendInput = !workflowState.isRunning || workflowState.isWorkflowComplete;\r\n domElements.sendUserMessageBtn.disabled = !canSendInput;\r\n \r\n // Update label based on state\r\n if (!domElements.sendUserMessageBtn.querySelector('.fa-spinner')) {\r\n if (workflowState.isWorkflowComplete || !workflowState.isRunning) {\r\n domElements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-play\"></i> Start';\r\n } else {\r\n domElements.sendUserMessageBtn.innerHTML = '<i class=\"fas fa-paper-plane\"></i> Send';\r\n }\r\n }\r\n }\r\n \r\n // Update input field state\r\n if (domElements.userMessageInput) {\r\n domElements.userMessageInput.disabled = workflowState.isRunning && !workflowState.isWorkflowComplete;\r\n }\r\n}\r\n\r\n/**\r\n * Verbesserte Protokoll-Rendering-Funktion mit Markdown-ähnlicher Formatierung\r\n * und Fortschrittsanzeige für Agenten\r\n */\r\nfunction renderLogs(logs) {\r\n // Verbesserte Container-Auswahl mit Fallback\r\n const logContainer = domElements.executionLog || document.getElementById('execution-log');\r\n \r\n if (!logContainer) {\r\n console.error(\"Execution log container nicht gefunden - Logs können nicht angezeigt werden\");\r\n return;\r\n }\r\n \r\n // Debug-Logging\r\n console.log(`Rendering ${logs ? logs.length : 0} Logs`);\r\n \r\n if (!logs || logs.length === 0) {\r\n logContainer.innerHTML = '<div class=\"log-empty-state\">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>';\r\n return;\r\n }\r\n\r\n // Logs HTML vorbereiten\r\n let logsHTML = '';\r\n \r\n try {\r\n logs.forEach(log => {\r\n // Sicherheitsprüfung für jedes Log-Objekt\r\n if (!log) return;\r\n \r\n const logId = log.id || `log_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;\r\n const logType = log.type || 'info';\r\n const logMessage = log.message || 'Keine Nachricht';\r\n const logTimestamp = log.timestamp ? new Date(log.timestamp).toLocaleTimeString() : new Date().toLocaleTimeString();\r\n const logAgent = log.agentName ? `<span class=\"log-agent\">[${log.agentName}]</span>` : '';\r\n const logWaiting = log.waiting ? '<span class=\"waiting-dots\"></span>' : '';\r\n \r\n // Zusätzliche CSS-Klassen für spezielle Hervorhebung\r\n const highlightClass = log.highlighted ? 'highlighted' : '';\r\n // Spezielle Agentenfarben basierend auf dem Agentennamen\r\n const agentColorClass = log.agentName ? `agent-${log.agentName.toLowerCase().replace(/\\s+/g, '-')}` : '';\r\n \r\n // Add progress indicator for agent logs when available\r\n let progressHTML = '';\r\n if (log.agentName && log.progress !== undefined) {\r\n const progressPercent = Math.round(log.progress * 100);\r\n progressHTML = `\r\n <div class=\"log-progress-container\">\r\n <div class=\"log-progress-bar\" style=\"width: ${progressPercent}%\"></div>\r\n <span class=\"log-progress-text\">${progressPercent}%</span>\r\n </div>\r\n `;\r\n }\r\n \r\n if (log.details) {\r\n logsHTML += `\r\n <div class=\"log-entry collapsible collapsed ${highlightClass} ${agentColorClass}\" data-log-id=\"${logId}\">\r\n <div class=\"log-header\">\r\n <span>\r\n <span class=\"log-time\">[${logTimestamp}]</span>\r\n ${logAgent}\r\n <span class=\"log-message log-${logType}\">${logMessage}</span>\r\n ${progressHTML}\r\n ${logWaiting}\r\n </span>\r\n <i class=\"fas fa-chevron-down toggle-icon\"></i>\r\n </div>\r\n <div class=\"log-content\">${WorkflowUtils.formatMarkdownLike(log.details)}</div>\r\n </div>\r\n `;\r\n } else {\r\n logsHTML += `\r\n <div class=\"log-entry ${highlightClass} ${agentColorClass}\" data-log-id=\"${logId}\">\r\n <span class=\"log-time\">[${logTimestamp}]</span>\r\n ${logAgent}\r\n <span class=\"log-message log-${logType}\">${logMessage}</span>\r\n ${progressHTML}\r\n ${logWaiting}\r\n </div>\r\n `;\r\n }\r\n });\r\n \r\n logContainer.innerHTML = logsHTML;\r\n \r\n // Event-Listener für aufklappbare Logs hinzufügen\r\n logContainer.querySelectorAll('.log-entry.collapsible .log-header').forEach(header => {\r\n header.addEventListener('click', () => {\r\n header.parentElement.classList.toggle('collapsed');\r\n });\r\n });\r\n \r\n // Zusätzliches CSS für die Agent-Hervorhebungen und Fortschrittsanzeige hinzufügen\r\n if (!document.getElementById('agent-log-styles')) {\r\n const styleEl = document.createElement('style');\r\n styleEl.id = 'agent-log-styles';\r\n styleEl.textContent = `\r\n .log-entry.highlighted {\r\n border-left: 3px solid #3498db;\r\n background-color: rgba(52, 152, 219, 0.1);\r\n }\r\n \r\n .log-entry.agent-moderator {\r\n border-left: 3px solid #9b59b6;\r\n }\r\n \r\n .log-entry.agent-user-agent {\r\n border-left: 3px solid #2ecc71;\r\n }\r\n \r\n .log-entry.agent-analysis-agent {\r\n border-left: 3px solid #f39c12;\r\n }\r\n \r\n .log-entry.agent-coder {\r\n border-left: 3px solid #e74c3c;\r\n }\r\n \r\n .log-entry.agent-assistant {\r\n border-left: 3px solid #1abc9c;\r\n }\r\n \r\n .log-success {\r\n color: #2ecc71;\r\n font-weight: bold;\r\n }\r\n \r\n .log-error {\r\n color: #e74c3c;\r\n font-weight: bold;\r\n }\r\n \r\n .log-warning {\r\n color: #f39c12;\r\n font-weight: bold;\r\n }\r\n \r\n .log-info {\r\n color: #3498db;\r\n }\r\n \r\n .log-progress-container {\r\n width: 100px;\r\n height: 8px;\r\n background-color: #eee;\r\n border-radius: 4px;\r\n margin: 0 10px;\r\n display: inline-block;\r\n vertical-align: middle;\r\n position: relative;\r\n }\r\n \r\n .log-progress-bar {\r\n height: 100%;\r\n background-color: #3498db;\r\n border-radius: 4px;\r\n }\r\n \r\n .log-progress-text {\r\n position: absolute;\r\n right: -35px;\r\n top: -2px;\r\n font-size: 10px;\r\n color: #777;\r\n }\r\n \r\n .waiting-dots {\r\n display: inline-block;\r\n width: 24px;\r\n height: 16px;\r\n text-align: left;\r\n font-weight: bold;\r\n color: #3498db;\r\n margin-left: 5px;\r\n }\r\n `;\r\n document.head.appendChild(styleEl);\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Rendern der Logs:\", error);\r\n logContainer.innerHTML = `\r\n <div class=\"log-entry log-error\">\r\n <span class=\"log-time\">[${new Date().toLocaleTimeString()}]</span>\r\n <span class=\"log-message\">Fehler beim Anzeigen der Logs: ${error.message}</span>\r\n </div>\r\n `;\r\n }\r\n \r\n WorkflowCoordination.updateWaitingDots();\r\n logContainer.scrollTop = logContainer.scrollHeight;\r\n}\r\n \r\n/**\r\n * Rendert Chat-Nachrichten mit angehängten Dateien und Löschfunktionalität\r\n * Updated to handle new document structure\r\n */\r\nfunction renderChatMessages(chatMessages, currentWorkflowId) {\r\n // Verbesserte Container-Auswahl mit Fallbacks\r\n const chatContainer = domElements.agentChatMessages || document.getElementById('agent-chat-messages');\r\n const emptyStateContainer = domElements.emptyChatState || document.getElementById('empty-chat-state');\r\n \r\n if (!chatContainer) {\r\n console.error(\"Chat messages container nicht gefunden - Nachrichten können nicht angezeigt werden\");\r\n return;\r\n }\r\n \r\n // Debug-Logging\r\n console.log(`Rendering ${chatMessages ? chatMessages.length : 0} Chat-Nachrichten für Workflow ${currentWorkflowId}`);\r\n \r\n if (!chatMessages || chatMessages.length === 0) {\r\n if (emptyStateContainer) {\r\n emptyStateContainer.style.display = 'flex';\r\n }\r\n chatContainer.style.display = 'none';\r\n return;\r\n }\r\n\r\n if (emptyStateContainer) {\r\n emptyStateContainer.style.display = 'none';\r\n }\r\n \r\n chatContainer.style.display = 'block';\r\n chatContainer.innerHTML = '';\r\n\r\n try {\r\n chatMessages.forEach((message, index) => {\r\n if (!message) {\r\n console.warn(`Nachricht an Position ${index} ist undefined oder null`);\r\n return;\r\n }\r\n \r\n const messageDiv = document.createElement('div');\r\n \r\n if (message.type === 'system') {\r\n messageDiv.className = 'chat-message system-message';\r\n messageDiv.innerHTML = `<div class=\"message-content\">${message.content || 'Keine Nachricht'}</div>`;\r\n } else {\r\n const isLastMessage = index === chatMessages.length - 1;\r\n const isCollapsed = !isLastMessage;\r\n const isUserMessage = message.agent_name === 'User Agent';\r\n \r\n // Passende Stile anwenden\r\n messageDiv.className = `chat-message agent-message ${isCollapsed ? 'collapsed' : ''} ${isUserMessage ? 'user' : ''}`;\r\n \r\n // Spezielle Verarbeitung für Moderator-Nachrichten an den Benutzer\r\n const isModeratorToUser = message.content && message.content.includes('[Moderator zu User Agent]');\r\n if (isModeratorToUser) {\r\n messageDiv.classList.add('moderator-to-user');\r\n }\r\n \r\n // Inhalt formatieren\r\n let formattedContent = message.content || '';\r\n \r\n // Lösch-Button für Nachrichten\r\n let deleteButton = '';\r\n if (currentWorkflowId && message.id) {\r\n deleteButton = `\r\n <div class=\"message-delete-container\">\r\n <button class=\"message-delete-btn\" data-message-id=\"${message.id}\" data-workflow-id=\"${currentWorkflowId}\" title=\"Nachricht löschen\">\r\n <i class=\"fas fa-times\"></i>\r\n </button>\r\n </div>\r\n `;\r\n }\r\n \r\n // Angehängte Dateien rendern - updated for new document structure\r\n let filesHTML = '';\r\n if (message.documents && message.documents.length > 0) {\r\n filesHTML = `\r\n <div class=\"message-files\">\r\n <div class=\"files-heading\">Angehängte Dateien (${message.documents.length}):</div>\r\n <ul class=\"files-list\">\r\n `;\r\n \r\n message.documents.forEach(doc => {\r\n let fileId, fileName, fileType, fileIcon;\r\n \r\n // Handle both old and new document structures\r\n if (doc.source && doc.source.type === 'file') {\r\n fileId = doc.source.id;\r\n fileName = doc.source.name || 'Unbekannte Datei';\r\n fileType = doc.source.content_type || '';\r\n } else if (doc.id && doc.type === 'file') {\r\n // Legacy format\r\n fileId = doc.id;\r\n fileName = doc.name || 'Unbekannte Datei';\r\n fileType = doc.content_type || '';\r\n } else if (doc.id) {\r\n // Fallback if structure unclear\r\n fileId = doc.id;\r\n fileName = 'Dokument';\r\n fileType = '';\r\n } else {\r\n // Skip if no valid ID found\r\n return;\r\n }\r\n \r\n fileIcon = WorkflowUtils.getFileTypeIcon({content_type: fileType});\r\n \r\n filesHTML += `\r\n <li class=\"file-item\">\r\n <i class=\"fas ${fileIcon}\"></i>\r\n <span class=\"file-name\">${fileName}</span>\r\n <div class=\"file-actions\">\r\n <button class=\"preview-file-btn\" data-file-id=\"${fileId}\" title=\"Vorschau\">\r\n <i class=\"fas fa-eye\"></i>\r\n </button>\r\n <button class=\"delete-file-btn\" data-file-id=\"${fileId}\" data-message-id=\"${message.id}\" title=\"Datei löschen\">\r\n <i class=\"fas fa-times\"></i>\r\n </button>\r\n </div>\r\n </li>\r\n `;\r\n });\r\n \r\n filesHTML += `\r\n </ul>\r\n </div>\r\n `;\r\n }\r\n \r\n // Moderator-Formatierung für bessere Anzeige\r\n if (isModeratorToUser) {\r\n formattedContent = formattedContent.replace('[Moderator zu User Agent]', '').trim();\r\n messageDiv.innerHTML = `\r\n <div class=\"message-header\">\r\n <span class=\"agent-name\">Moderator fragt User Agent</span>\r\n <span class=\"message-time\">${new Date(message.timestamp || new Date()).toLocaleTimeString()}</span>\r\n ${deleteButton}\r\n </div>\r\n <div class=\"message-content\">${WorkflowUtils.formatMarkdownLike(formattedContent)}</div>\r\n ${filesHTML}\r\n <div class=\"toggle-content\">${isCollapsed ? 'Mehr anzeigen' : 'Weniger anzeigen'}</div>\r\n `;\r\n } else {\r\n messageDiv.innerHTML = `\r\n <div class=\"message-header\">\r\n <span class=\"agent-name\">${message.agent_name || 'Unbekannter Agent'}</span>\r\n <span class=\"message-time\">${new Date(message.timestamp || new Date()).toLocaleTimeString()}</span>\r\n ${deleteButton}\r\n </div>\r\n <div class=\"message-content\">${WorkflowUtils.formatMarkdownLike(formattedContent)}</div>\r\n ${filesHTML}\r\n <div class=\"toggle-content\">${isCollapsed ? 'Mehr anzeigen' : 'Weniger anzeigen'}</div>\r\n `;\r\n }\r\n }\r\n \r\n chatContainer.appendChild(messageDiv);\r\n });\r\n \r\n // Event-Listener für Toggle-Buttons und Dateiaktionen hinzufügen\r\n setupToggleButtons(chatContainer);\r\n setupFileActions(chatContainer, chatMessages, currentWorkflowId);\r\n setupDeleteButtons(chatContainer, currentWorkflowId);\r\n } catch (error) {\r\n console.error(\"Fehler beim Rendern der Chat-Nachrichten:\", error);\r\n chatContainer.innerHTML = `\r\n <div class=\"chat-message system-message error\">\r\n <div class=\"message-content\">Fehler beim Anzeigen der Nachrichten: ${error.message}</div>\r\n </div>\r\n `;\r\n }\r\n \r\n // Zum Ende scrollen\r\n chatContainer.scrollTop = chatContainer.scrollHeight;\r\n}\r\n\r\n/**\r\n * Richtet Dateiaktionen (Vorschau und Löschen) ein\r\n * Updated to handle new document structure\r\n */\r\nfunction setupFileActions(container, chatMessages, currentWorkflowId) {\r\n // Beenden, wenn keine Workflow-ID vorhanden ist\r\n if (!currentWorkflowId) return;\r\n \r\n // Dateivorschau-Schaltflächen\r\n container.querySelectorAll('.preview-file-btn').forEach(btn => {\r\n btn.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n \r\n const fileId = btn.getAttribute('data-file-id');\r\n if (!fileId) {\r\n console.error(\"Keine Datei-ID für Vorschau gefunden\");\r\n return;\r\n }\r\n \r\n console.log(`Vorschau der Datei: ${fileId}`);\r\n \r\n // Improved file lookup algorithm for the new document structure\r\n let file = null;\r\n let documentObj = null;\r\n \r\n // 1. Try to find in globalState with different ID formats\r\n if (window.globalState && window.globalState.mainView && window.globalState.mainView.availableFiles) {\r\n // Try exact match first\r\n file = window.globalState.mainView.availableFiles.find(f => f.id === fileId);\r\n \r\n // Then try partial match (file ID might be prefixed with \"file_\")\r\n if (!file) {\r\n file = window.globalState.mainView.availableFiles.find(f => \r\n (typeof f.id === 'string' && typeof fileId === 'string' &&\r\n (fileId.includes(f.id) || f.id.includes(fileId)))\r\n );\r\n }\r\n \r\n if (file) console.log(`Datei in globalState gefunden: ${file.name}`);\r\n }\r\n \r\n // 2. Extract from message documents if not found in globalState\r\n if (!file) {\r\n // Search through all messages\r\n for (const message of chatMessages) {\r\n if (!message.documents) continue;\r\n \r\n // Process all documents to find the file\r\n for (const doc of message.documents) {\r\n // Check if doc ID matches or contains the target fileId\r\n if ((doc.id === fileId) || (doc.id && doc.id.includes(fileId))) {\r\n documentObj = doc; // Store the document object\r\n \r\n if (doc.source) {\r\n // Create file object from document source\r\n file = {\r\n id: doc.id, // Verwende die Dokument-ID für die UI\r\n name: doc.source.name || \"Unbekannte Datei\",\r\n content_type: doc.source.content_type,\r\n size: doc.source.size,\r\n source: doc.source, // Wichtig: Die Quelle beibehalten\r\n contents: doc.contents, // Wichtig: Die Inhalte beibehalten\r\n content: doc.content // Alternativ auch content-Property kopieren, falls vorhanden\r\n };\r\n console.log(`Datei aus Dokument-ID erstellt: ${file.name}`, file);\r\n \r\n // Prüfen, ob es sich um ein generiertes Dokument handelt\r\n if (doc.source.type === \"generated\") {\r\n console.log(\"Generiertes Dokument gefunden, überprüfe Inhalt:\", !!doc.contents);\r\n }\r\n \r\n break;\r\n } }\r\n \r\n // Check if source ID matches or contains the target fileId\r\n if (doc.source && ((doc.source.id === fileId) || (doc.source.id && doc.source.id.includes(fileId)))) {\r\n documentObj = doc; // Store the document object\r\n \r\n file = {\r\n id: doc.source.id,\r\n name: doc.source.name || \"Unbekannte Datei\",\r\n content_type: doc.source.content_type,\r\n size: doc.source.size,\r\n source: doc.source\r\n };\r\n console.log(`Datei aus Dokument-Source erstellt: ${file.name}`);\r\n break;\r\n }\r\n }\r\n \r\n if (file) break;\r\n }\r\n }\r\n \r\n if (file) {\r\n // If we found a document object, use it for preview\r\n if (documentObj) {\r\n // Pass the document object with its source\r\n previewFile({\r\n ...file,\r\n id: fileId, // Keep the original document ID\r\n source: file.source || documentObj.source // Ensure source is available\r\n });\r\n } else {\r\n // Just use the file object\r\n previewFile(file);\r\n }\r\n \r\n // Download- und Kopier-Schaltflächen aktivieren\r\n if (WorkflowCoordination.userInputState.domElements.downloadFileBtn) {\r\n WorkflowCoordination.userInputState.domElements.downloadFileBtn.disabled = false;\r\n }\r\n if (WorkflowCoordination.userInputState.domElements.copyFileBtn) {\r\n WorkflowCoordination.userInputState.domElements.copyFileBtn.disabled = false;\r\n }\r\n } else {\r\n console.error(`Datei mit ID ${fileId} nicht für Vorschau gefunden`);\r\n showToast(\"Vorschau nicht möglich\", \"Die Datei konnte nicht gefunden werden.\", \"error\");\r\n }\r\n });\r\n });\r\n \r\n // Datei-Lösch-Schaltflächen\r\n container.querySelectorAll('.delete-file-btn').forEach(btn => {\r\n btn.addEventListener('click', async (e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n \r\n const fileId = btn.getAttribute('data-file-id');\r\n const messageId = btn.getAttribute('data-message-id');\r\n \r\n if (!fileId || !messageId) {\r\n console.error(\"Keine Datei-ID oder Nachrichten-ID für Löschung gefunden\");\r\n return;\r\n }\r\n \r\n console.log(`Lösche Datei: ${fileId} aus Nachricht: ${messageId}`);\r\n \r\n try {\r\n // Find the message to get file details before deletion\r\n const message = chatMessages.find(msg => msg.id === messageId);\r\n if (!message) {\r\n throw new Error(`Nachricht mit ID ${messageId} nicht gefunden`);\r\n }\r\n \r\n // Improved approach: First locate the file to get details before deletion\r\n let fileName = \"Datei\";\r\n let exactFileId = fileId;\r\n \r\n // Find file in message documents with improved matching\r\n if (message.documents) {\r\n const fileDoc = message.documents.find(d => \r\n (d.id === fileId) || \r\n (d.id && typeof d.id === 'string' && typeof fileId === 'string' && \r\n (d.id.includes(fileId) || fileId.includes(d.id))) ||\r\n (d.source && d.source.id === fileId) || \r\n (d.source && d.source.id && typeof d.source.id === 'string' && \r\n typeof fileId === 'string' && \r\n (d.source.id.includes(fileId) || fileId.includes(d.source.id)))\r\n );\r\n \r\n if (fileDoc) {\r\n if (fileDoc.source) {\r\n fileName = fileDoc.source.name || \"Datei\";\r\n // Use the most specific ID we can find\r\n exactFileId = fileDoc.source.id || fileDoc.id || fileId;\r\n } else if (fileDoc.id) {\r\n fileName = \"Dokument\";\r\n exactFileId = fileDoc.id;\r\n }\r\n }\r\n }\r\n \r\n // API aufrufen, um die Datei zu löschen\r\n const success = await deleteFileFromMessage(\r\n currentWorkflowId,\r\n messageId,\r\n exactFileId\r\n );\r\n \r\n if (success) {\r\n console.log(`Datei ${exactFileId} erfolgreich gelöscht`);\r\n \r\n // Aus dem documents-Array der Nachricht entfernen\r\n const messageIndex = chatMessages.findIndex(msg => msg.id === messageId);\r\n if (messageIndex !== -1 && chatMessages[messageIndex].documents) {\r\n // Use improved matching for file deletion\r\n chatMessages[messageIndex].documents = chatMessages[messageIndex].documents.filter(doc => {\r\n // Check document ID\r\n if (doc.id === exactFileId) return false;\r\n if (doc.id && typeof doc.id === 'string' && typeof exactFileId === 'string' &&\r\n (doc.id.includes(exactFileId) || exactFileId.includes(doc.id))) return false;\r\n \r\n // Check source ID\r\n if (doc.source && doc.source.id === exactFileId) return false;\r\n if (doc.source && doc.source.id && typeof doc.source.id === 'string' && \r\n typeof exactFileId === 'string' &&\r\n (doc.source.id.includes(exactFileId) || exactFileId.includes(doc.source.id))) return false;\r\n \r\n return true;\r\n });\r\n \r\n // Refresh the message display\r\n renderChatMessages(chatMessages, currentWorkflowId);\r\n }\r\n \r\n // Erfolgsmeldung anzeigen\r\n showToast(\"Erfolg\", `${fileName} wurde erfolgreich gelöscht`, \"success\");\r\n } else {\r\n console.error(`Fehler beim Löschen der Datei ${exactFileId}`);\r\n showToast(\"Löschen fehlgeschlagen\", \"Die Datei konnte nicht gelöscht werden.\", \"error\");\r\n }\r\n } catch (error) {\r\n console.error(`Fehler beim Löschen der Datei: ${error.message}`);\r\n showToast(\"Fehler\", `Löschen fehlgeschlagen: ${error.message}`, \"error\");\r\n }\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Setup message deletion buttons\r\n */\r\nfunction setupDeleteButtons(container, workflowId) {\r\n container.querySelectorAll('.message-delete-btn').forEach(btn => {\r\n btn.addEventListener('click', async (e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n \r\n const messageId = btn.getAttribute('data-message-id');\r\n \r\n if (confirm('Möchten Sie diese Nachricht wirklich löschen?')) {\r\n try {\r\n const success = await WorkflowData.deleteWorkflowMessage(workflowId, messageId);\r\n if (success) {\r\n console.log(`Nachricht ${messageId} erfolgreich gelöscht`);\r\n \r\n // Nachricht aus dem DOM entfernen\r\n const messageElement = btn.closest('.chat-message');\r\n if (messageElement && messageElement.parentNode) {\r\n messageElement.parentNode.removeChild(messageElement);\r\n }\r\n \r\n showToast(\"Erfolg\", \"Nachricht wurde gelöscht\", \"success\");\r\n } else {\r\n console.error(`Fehler beim Löschen der Nachricht ${messageId}`);\r\n showToast(\"Löschen fehlgeschlagen\", \"Die Nachricht konnte nicht gelöscht werden\", \"error\");\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Löschen der Nachricht:\", error);\r\n showToast(\"Fehler\", `Löschen fehlgeschlagen: ${error.message}`, \"error\");\r\n }\r\n }\r\n });\r\n });\r\n}\r\n\r\n\r\n/**\r\n * Setup toggle buttons for collapsible chat messages\r\n */\r\nfunction setupToggleButtons(container) {\r\n container.querySelectorAll('.toggle-content').forEach(toggle => {\r\n toggle.addEventListener('click', (e) => {\r\n const messageDiv = e.target.closest('.chat-message');\r\n if (messageDiv) {\r\n messageDiv.classList.toggle('collapsed');\r\n e.target.textContent = messageDiv.classList.contains('collapsed') ? 'Mehr anzeigen' : 'Weniger anzeigen';\r\n }\r\n });\r\n });\r\n}\r\n\r\n\r\n/**\r\n * Helper function to copy text content to clipboard\r\n */\r\nasync function copyTextContent(contentBase64) {\r\n try {\r\n const textContent = atob(contentBase64);\r\n \r\n // In Zwischenablage kopieren\r\n try {\r\n await navigator.clipboard.writeText(textContent);\r\n showToast(\"Erfolg\", \"Inhalt in Zwischenablage kopiert\", \"success\");\r\n } catch (clipboardError) {\r\n console.error(\"Fehler beim Zugriff auf die Zwischenablage:\", clipboardError);\r\n \r\n // Fallback-Methode über temporäres Textarea-Element\r\n const textarea = document.createElement('textarea');\r\n textarea.value = textContent;\r\n textarea.style.position = 'fixed';\r\n textarea.style.opacity = '0';\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n \r\n try {\r\n const successful = document.execCommand('copy');\r\n if (successful) {\r\n showToast(\"Erfolg\", \"Inhalt in Zwischenablage kopiert\", \"success\");\r\n } else {\r\n throw new Error(\"execCommand('copy') fehlgeschlagen\");\r\n }\r\n } catch (execError) {\r\n console.error(\"Fallback-Kopiermethode fehlgeschlagen:\", execError);\r\n showToast(\"Fehler\", \"Kopieren in die Zwischenablage fehlgeschlagen\", \"error\");\r\n } finally {\r\n document.body.removeChild(textarea);\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing text content:\", error);\r\n throw error;\r\n }\r\n}\r\n\r\n\r\n\r\n/**\r\n * Initiates a file download with the given content\r\n * @param {string} content - Base64 encoded file content\r\n * @param {string} fileName - Name of the file\r\n * @param {string} contentType - MIME type of the file\r\n */\r\nfunction initiateDownload(content, fileName, contentType) {\r\n try {\r\n // Convert base64 to blob\r\n const byteCharacters = atob(content);\r\n const byteArrays = [];\r\n \r\n for (let i = 0; i < byteCharacters.length; i += 512) {\r\n const slice = byteCharacters.slice(i, i + 512);\r\n \r\n const byteNumbers = new Array(slice.length);\r\n for (let j = 0; j < slice.length; j++) {\r\n byteNumbers[j] = slice.charCodeAt(j);\r\n }\r\n \r\n const byteArray = new Uint8Array(byteNumbers);\r\n byteArrays.push(byteArray);\r\n }\r\n \r\n const blob = new Blob(byteArrays, { type: contentType || 'application/octet-stream' });\r\n \r\n // Create download link\r\n const url = window.URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.style.display = 'none';\r\n a.href = url;\r\n a.download = fileName || 'download';\r\n \r\n // Append to body, trigger click, then remove\r\n document.body.appendChild(a);\r\n a.click();\r\n \r\n // Clean up\r\n setTimeout(() => {\r\n document.body.removeChild(a);\r\n window.URL.revokeObjectURL(url);\r\n }, 100);\r\n \r\n return true;\r\n } catch (error) {\r\n console.error(\"Error initiating download:\", error);\r\n showToast(\"Download Error\", error.message, \"error\");\r\n return false;\r\n }\r\n}\r\n/**\r\n * Zeigt eine Dateivorschau an - Verbesserte Version\r\n */\r\nfunction previewFile(file) {\r\n if (!file) {\r\n console.error(\"Kann Datei nicht in Vorschau anzeigen: Datei ist null\");\r\n return;\r\n }\r\n\r\n // Verwende domElements oder direkten DOM-Zugriff mit Fallback\r\n const previewContainer = domElements.filePreviewContainer || document.getElementById('file-preview-container');\r\n const previewContent = domElements.filePreviewContent || document.getElementById('file-preview-content');\r\n \r\n if (!previewContent) {\r\n console.error(\"Kann Datei nicht in Vorschau anzeigen: Vorschaucontainer nicht gefunden\");\r\n return;\r\n }\r\n\r\n // Make preview container visible\r\n if (previewContainer) {\r\n previewContainer.style.display = 'flex';\r\n }\r\n\r\n console.log(`Zeige Vorschau der Datei: ${file.name} (ID: ${file.id}, Typ: ${file.content_type})`);\r\n \r\n // Set current preview file for download/copy actions\r\n currentPreviewFile = file;\r\n \r\n try {\r\n // Ladeindikator anzeigen\r\n previewContent.innerHTML = `\r\n <div class=\"loading-preview\">\r\n <i class=\"fas fa-spinner fa-spin\"></i>\r\n <span>Datei wird geladen...</span>\r\n </div>\r\n `;\r\n \r\n // Download- und Kopier-Schaltflächen aktivieren\r\n const downloadBtn = domElements.downloadFileBtn || document.getElementById('download-file-btn');\r\n const copyBtn = domElements.copyFileBtn || document.getElementById('copy-file-btn');\r\n \r\n if (downloadBtn) downloadBtn.disabled = false;\r\n if (copyBtn) copyBtn.disabled = false;\r\n \r\n // WICHTIG: Prüfen, ob es sich um ein generiertes Dokument handelt\r\n const isGeneratedDoc = file.source && file.source.type === \"generated\";\r\n \r\n if (isGeneratedDoc) {\r\n console.log(\"Zeige generiertes Dokument: Inhalte direkt aus dem Dokument verwenden\");\r\n console.log(\"File object:\", file);\r\n console.log(\"Has contents property:\", !!file.contents);\r\n \r\n if (file.contents && file.contents.length > 0) {\r\n console.log(\"Contents array length:\", file.contents.length);\r\n console.log(\"First content item:\", file.contents[0]);\r\n const content = file.contents[0].text || \"\";\r\n console.log(\"Content text length:\", content.length);\r\n \r\n // Text-Vorschau anzeigen\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.escapeHtml(content)}</pre>\r\n `;\r\n \r\n return; // Früh beenden, da wir den Inhalt bereits haben\r\n } else {\r\n // Überprüfen Sie, ob die Inhalte möglicherweise woanders sind\r\n console.log(\"Contents not found in expected location, checking alternatives\");\r\n \r\n // Alternative 1: Prüfen, ob das Dokument selbst ein Text-Feld hat\r\n if (file.text) {\r\n console.log(\"Found text directly in file object\");\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.escapeHtml(file.text)}</pre>\r\n `;\r\n return;\r\n }\r\n \r\n // Alternative 2: Prüfen, ob der Inhalt in source.contents sein könnte\r\n if (file.source && file.source.contents) {\r\n console.log(\"Found contents in source.contents\");\r\n const sourceContents = file.source.contents;\r\n if (Array.isArray(sourceContents) && sourceContents.length > 0) {\r\n const content = sourceContents[0].text || \"\";\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.escapeHtml(content)}</pre>\r\n `;\r\n return;\r\n }\r\n }\r\n \r\n // Alternative 3: Prüfen, ob das content direkt in document.content sein könnte\r\n if (file.content) {\r\n console.log(\"Found content directly in file.content\");\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.escapeHtml(file.content)}</pre>\r\n `;\r\n return;\r\n }\r\n \r\n // Keine Inhalte gefunden\r\n showFileErrorState(previewContent, \"Generiertes Dokument enthält keine Inhalte. Struktur: \" + JSON.stringify(Object.keys(file)));\r\n return;\r\n }\r\n }\r\n\r\n // Für normale Dateien: API-Aufruf notwendig\r\n const actualFileId = file.source && file.source.type === \"file\" && file.source.id ? \r\n file.source.id : file.id;\r\n \r\n console.log(`Lade Datei über API: ${actualFileId}`);\r\n \r\n // Dateidaten vom Backend abrufen\r\n window.globalUtils.readApiData(`/api/files/${actualFileId}/download`)\r\n .then(fileData => {\r\n // Vorschau basierend auf Dateityp anzeigen\r\n if (!fileData || !fileData.content) {\r\n // If the exact file ID didn't work, try to find file with partial matching\r\n if (window.globalState && window.globalState.mainView && \r\n window.globalState.mainView.availableFiles) {\r\n \r\n // Find a close match based on partial ID\r\n const partialMatch = window.globalState.mainView.availableFiles.find(f => \r\n f.id && actualFileId && (\r\n f.id.includes(actualFileId) || \r\n actualFileId.includes(f.id)\r\n ));\r\n \r\n if (partialMatch) {\r\n console.log(`Found partial match for file: ${partialMatch.id}`);\r\n // Retry with partial match ID\r\n window.globalUtils.readApiData(`/api/files/${partialMatch.id}/download`)\r\n .then(retryData => {\r\n renderFilePreview(retryData, file, previewContent);\r\n })\r\n .catch(retryError => {\r\n console.error(\"Retry also failed:\", retryError);\r\n showFileErrorState(previewContent, \"Datei konnte nicht geladen werden.\");\r\n });\r\n return;\r\n }\r\n }\r\n \r\n // If no match found, show error\r\n showFileErrorState(previewContent, \"Keine Daten verfügbar\");\r\n return;\r\n }\r\n \r\n renderFilePreview(fileData, file, previewContent);\r\n })\r\n .catch(error => {\r\n console.error(\"Fehler beim Laden der Dateivorschau:\", error);\r\n showFileErrorState(previewContent, error.message);\r\n });\r\n } catch (error) {\r\n console.error(\"Fehler in der previewFile-Funktion:\", error);\r\n showFileErrorState(previewContent, error.message);\r\n }\r\n}\r\n\r\n/**\r\n * Aktuelle Vorschau-Datei herunterladen\r\n * Updated for more robust file handling\r\n */\r\nasync function downloadPreviewFile() {\r\n if (!currentPreviewFile) return;\r\n \r\n try {\r\n const file = currentPreviewFile;\r\n \r\n // Für generierte Dokumente\r\n if (file.source && file.source.type === \"generated\" && file.contents && file.contents.length > 0) {\r\n const content = file.contents[0].text || \"\";\r\n \r\n // Erzeuge einen Blob aus dem Textinhalt\r\n const blob = new Blob([content], { type: file.content_type || 'text/plain' });\r\n \r\n // Download-Link erstellen\r\n const url = window.URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.style.display = 'none';\r\n a.href = url;\r\n a.download = file.name || 'document.txt';\r\n \r\n // Anhängen, klicken und aufräumen\r\n document.body.appendChild(a);\r\n a.click();\r\n \r\n setTimeout(() => {\r\n document.body.removeChild(a);\r\n window.URL.revokeObjectURL(url);\r\n }, 100);\r\n \r\n return;\r\n }\r\n \r\n // Für normale Dateien den bisherigen Code verwenden...\r\n const actualFileId = file.source && file.source.type === \"file\" && file.source.id ? \r\n file.source.id : file.id;\r\n console.log(`Downloading file: ${file.name} (ID: ${actualFileId})`);\r\n const response = await window.globalUtils.readApiData(`/api/files/${actualFileId}/download`);\r\n\r\n if (!response || !response.content) {\r\n // If direct download failed, try to find a matching file ID\r\n if (window.globalState && window.globalState.mainView && \r\n window.globalState.mainView.availableFiles) {\r\n \r\n // Find alternative file ID by partial match\r\n const alternativeFile = window.globalState.mainView.availableFiles.find(f => \r\n f.id && actualFileId && (f.id.includes(actualFileId) || actualFileId.includes(f.id))\r\n );\r\n \r\n if (alternativeFile) {\r\n console.log(`Trying alternative file ID: ${alternativeFile.id}`);\r\n const altResponse = await window.globalUtils.readApiData(\r\n `/api/files/${alternativeFile.id}/download`\r\n );\r\n \r\n if (altResponse && altResponse.content) {\r\n // Use the alternative file\r\n initiateDownload(altResponse.content, alternativeFile.name, alternativeFile.content_type);\r\n return;\r\n }\r\n }\r\n }\r\n \r\n showToast(\"Fehler\", \"Keine Dateidaten verfügbar\", \"error\");\r\n return;\r\n }\r\n \r\n // Download with the original file if available\r\n initiateDownload(response.content, file.name, file.content_type);\r\n \r\n } catch (error) {\r\n console.error(\"Fehler beim Herunterladen der Datei:\", error);\r\n showToast(\"Fehler\", `Download fehlgeschlagen: ${error.message}`, \"error\");\r\n }\r\n}\r\n\r\n\r\n\r\n\r\n/**\r\n * Zeichnet die Liste der zusätzlichen Dateien während des Chats\r\n */\r\nfunction renderAdditionalFiles() {\r\n const container = document.getElementById('additional-files-container');\r\n if (!container) return;\r\n\r\n container.innerHTML = '';\r\n\r\n WorkflowCoordination.userInputState.additionalFiles.forEach((file, index) => {\r\n const fileElement = document.createElement('div');\r\n fileElement.className = 'additional-file-item';\r\n fileElement.innerHTML = `\r\n <i class=\"fas ${WorkflowUtils.getFileTypeIcon(file)}\"></i>\r\n ${file.name}\r\n <span class=\"remove-additional-file\" data-index=\"${index}\">\r\n <i class=\"fas fa-times\"></i>\r\n </span>\r\n `;\r\n\r\n // Add remove file functionality\r\n const removeBtn = fileElement.querySelector('.remove-additional-file');\r\n removeBtn.addEventListener('click', () => {\r\n WorkflowCoordination.userInputState.additionalFiles.splice(index, 1);\r\n renderAdditionalFiles();\r\n updatePromptVisualization();\r\n });\r\n\r\n container.appendChild(fileElement);\r\n });\r\n}\r\n\r\n\r\n\r\n/**\r\n * Aktualisiert die Visualisierung der Dateien im Prompt-Bereich\r\n * Enhanced for the refactored backend\r\n */\r\nfunction updatePromptVisualization() {\r\n const elements = WorkflowCoordination.userInputState.domElements;\r\n \r\n console.log(\"Aktualisiere Prompt-Visualisierung\");\r\n \r\n // Prüfen, ob ein Dateivisualisierungsbereich existiert\r\n let filePreviewArea = document.getElementById('selected-files-preview');\r\n \r\n if (!filePreviewArea) {\r\n console.log('Kein Dateivisualisierungsbereich gefunden, erstelle ihn');\r\n \r\n // Falls er noch nicht existiert, erstellen\r\n if (elements.userInputArea) {\r\n filePreviewArea = document.createElement('div');\r\n filePreviewArea.id = 'selected-files-preview';\r\n filePreviewArea.className = 'selected-files-preview';\r\n elements.userInputArea.appendChild(filePreviewArea);\r\n } else {\r\n console.error(\"userInputArea nicht gefunden, kann Dateivisualisierung nicht erstellen\");\r\n return;\r\n }\r\n }\r\n \r\n // Jetzt aktualisieren (ob neu erstellt oder bereits vorhanden)\r\n // Zurücksetzen\r\n filePreviewArea.innerHTML = '';\r\n \r\n // Wenn Dateien vorhanden sind\r\n if (WorkflowCoordination.userInputState.additionalFiles.length > 0) {\r\n const fileCount = WorkflowCoordination.userInputState.additionalFiles.length;\r\n \r\n console.log(`Visualisiere ${fileCount} angehängte Dateien`);\r\n \r\n // Header erstellen\r\n const header = document.createElement('div');\r\n header.className = 'files-preview-header';\r\n header.textContent = `${fileCount} ${fileCount === 1 ? 'Datei' : 'Dateien'} angehängt:`;\r\n filePreviewArea.appendChild(header);\r\n \r\n // Liste erstellen\r\n const fileList = document.createElement('ul');\r\n fileList.className = 'files-preview-list';\r\n \r\n // Dateien hinzufügen\r\n WorkflowCoordination.userInputState.additionalFiles.forEach(file => {\r\n const item = document.createElement('li');\r\n item.className = 'file-preview-item';\r\n item.innerHTML = `\r\n <i class=\"fas ${WorkflowUtils.getFileTypeIcon(file)}\"></i>\r\n <span>${file.name}</span>\r\n <button class=\"remove-file-btn\" data-file-id=\"${file.id}\">\r\n <i class=\"fas fa-times\"></i>\r\n </button>\r\n `;\r\n \r\n // Add click handler to remove button\r\n const removeBtn = item.querySelector('.remove-file-btn');\r\n if (removeBtn) {\r\n removeBtn.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n \r\n // Remove the file from additionalFiles\r\n const fileId = removeBtn.getAttribute('data-file-id');\r\n if (fileId) {\r\n WorkflowCoordination.userInputState.additionalFiles = WorkflowCoordination.userInputState.additionalFiles.filter(\r\n f => f.id !== fileId\r\n );\r\n // Update the visualization\r\n updatePromptVisualization();\r\n // Also update the file attachments list\r\n renderAdditionalFiles();\r\n }\r\n });\r\n }\r\n \r\n fileList.appendChild(item);\r\n });\r\n \r\n filePreviewArea.appendChild(fileList);\r\n filePreviewArea.style.display = 'block';\r\n } else {\r\n filePreviewArea.style.display = 'none';\r\n }\r\n \r\n // Wenn ein Chat-Nachrichten-Container existiert, auch dort aktualisieren\r\n updateChatMessageFiles();\r\n}\r\n\r\n\r\n/**\r\n * Aktualisiert die angehängten Dateien in der aktuellen Chat-Nachricht\r\n */\r\nfunction updateChatMessageFiles() {\r\n // Diese Funktion aktualisiert die Dateianzeige in der Chat-Nachricht,\r\n // falls eine aktive Chat-Nachricht existiert\r\n if (!workflowState || !workflowState.chatMessages || workflowState.chatMessages.length === 0) {\r\n console.log(\"Keine Chat-Nachrichten zum Aktualisieren vorhanden\");\r\n return;\r\n }\r\n \r\n console.log(\"Aktualisiere angehängte Dateien in Chat-Nachrichten\");\r\n \r\n // Letzte Chat-Nachricht holen\r\n const lastMessage = workflowState.chatMessages[workflowState.chatMessages.length - 1];\r\n \r\n // Prüfen, ob es sich um eine User-Nachricht handelt\r\n if (lastMessage.type === 'agent' && lastMessage.agent_name === 'User Agent') {\r\n console.log(\"Aktualisiere Dateianhänge in letzter User-Nachricht\");\r\n \r\n // Eine neue Nachricht erzeugen lassen, die die aktuellen Dateien enthält\r\n // Die addChatMessage-Funktion kümmert sich dann um das Hinzufügen der Dateien\r\n const updatedMessage = {\r\n ...lastMessage,\r\n documents: [] // Zurücksetzen, damit addChatMessage neue Dokumente hinzufügt\r\n };\r\n \r\n // Zähle die angehängten Dateien\r\n if (WorkflowCoordination.userInputState.additionalFiles.length > 0) {\r\n console.log(`${WorkflowCoordination.userInputState.additionalFiles.length} Dateien werden angehängt`);\r\n }\r\n \r\n // Nachricht aktualisieren\r\n WorkflowCoordination.addChatMessage(updatedMessage);\r\n } else {\r\n console.log(\"Letzte Nachricht ist keine User-Nachricht, keine Aktualisierung nötig\");\r\n }\r\n}\r\n\r\n\r\nasync function copyPreviewFileContent() {\r\n if (!currentPreviewFile) return;\r\n \r\n try {\r\n const file = currentPreviewFile;\r\n \r\n // Prüfen, ob es sich um eine Textdatei handelt\r\n const isTextFile = file.content_type && \r\n (file.content_type.toLowerCase().includes('text') || \r\n file.content_type.toLowerCase().includes('json') ||\r\n file.content_type.toLowerCase().includes('csv') ||\r\n file.content_type.toLowerCase().includes('markdown'));\r\n \r\n if (!isTextFile) {\r\n showToast(\"Hinweis\", \"Nur Textdateien können in die Zwischenablage kopiert werden.\", \"warning\");\r\n return;\r\n }\r\n \r\n // Für generierte Dokumente\r\n if (file.source && file.source.type === \"generated\" && file.contents && file.contents.length > 0) {\r\n const content = file.contents[0].text || \"\";\r\n \r\n // Moderne Methode zum Kopieren in die Zwischenablage\r\n try {\r\n // Clipboard API verwenden (moderne Browser)\r\n await navigator.clipboard.writeText(content);\r\n showToast(\"Erfolg\", \"Inhalt in Zwischenablage kopiert\", \"success\");\r\n } catch (clipboardError) {\r\n console.error(\"Fehler beim Zugriff auf die Zwischenablage:\", clipboardError);\r\n \r\n // Fallback für ältere Browser oder wenn die Clipboard API nicht verfügbar ist\r\n showToast(\"Fehler\", \"Kopieren in die Zwischenablage nicht möglich. Browser unterstützt diese Funktion nicht.\", \"error\");\r\n }\r\n \r\n return;\r\n }\r\n \r\n // Für normale Dateien\r\n const actualFileId = file.source && file.source.type === \"file\" && file.source.id ? \r\n file.source.id : file.id;\r\n \r\n // Datei abrufen\r\n const response = await window.globalUtils.readApiData(`/api/files/${actualFileId}/download`);\r\n \r\n if (!response || !response.content) {\r\n // Try alternative file ID lookup if direct retrieval fails\r\n if (window.globalState && window.globalState.mainView && \r\n window.globalState.mainView.availableFiles) {\r\n \r\n // Find a close match\r\n const alternativeFile = window.globalState.mainView.availableFiles.find(f => \r\n f.id && actualFileId && (f.id.includes(actualFileId) || actualFileId.includes(f.id))\r\n );\r\n \r\n if (alternativeFile) {\r\n console.log(`Trying alternative file ID for copy: ${alternativeFile.id}`);\r\n const altResponse = await window.globalUtils.readApiData(\r\n `/api/files/${alternativeFile.id}/download`\r\n );\r\n \r\n if (altResponse && altResponse.content) {\r\n // Moderne Methode verwenden\r\n try {\r\n const textContent = atob(altResponse.content);\r\n await navigator.clipboard.writeText(textContent);\r\n showToast(\"Erfolg\", \"Inhalt in Zwischenablage kopiert\", \"success\");\r\n } catch (error) {\r\n showToast(\"Fehler\", \"Kopieren in die Zwischenablage nicht möglich\", \"error\");\r\n }\r\n return;\r\n }\r\n }\r\n }\r\n \r\n showToast(\"Fehler\", \"Keine Dateidaten verfügbar\", \"error\");\r\n return;\r\n }\r\n \r\n // Moderne Methode verwenden\r\n try {\r\n const textContent = atob(response.content);\r\n await navigator.clipboard.writeText(textContent);\r\n showToast(\"Erfolg\", \"Inhalt in Zwischenablage kopiert\", \"success\");\r\n } catch (error) {\r\n showToast(\"Fehler\", \"Kopieren in die Zwischenablage nicht möglich\", \"error\");\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Kopieren der Datei:\", error);\r\n showToast(\"Fehler\", `Kopieren fehlgeschlagen: ${error.message}`, \"error\");\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Helper function to show error state in file preview\r\n */\r\nfunction showFileErrorState(previewContent, message) {\r\n previewContent.innerHTML = `\r\n <div class=\"error-state\">\r\n <i class=\"fas fa-exclamation-triangle\"></i>\r\n <h4>Fehler beim Laden der Datei</h4>\r\n <p>${message}</p>\r\n </div>\r\n `;\r\n}\r\n\r\n/**\r\n * Helper function to render file preview based on content type\r\n */\r\nfunction renderFilePreview(fileData, file, previewContent) {\r\n const mimeType = file.content_type ? file.content_type.toLowerCase() : '';\r\n \r\n // Check for generated document first\r\n const isGeneratedDoc = file.source && file.source.type === \"generated\";\r\n \r\n if (isGeneratedDoc) {\r\n console.log(\"Zeige generiertes Dokument: Inhalte direkt aus dem Dokument verwenden\");\r\n \r\n if (file.contents && file.contents.length > 0) {\r\n // Get the content text, with proper error handling\r\n let content = \"\";\r\n try {\r\n content = file.contents[0].text || \"\";\r\n \r\n // Apply proper escaping for HTML display\r\n const escapedContent = WorkflowUtils.escapeHtml(content);\r\n \r\n // Apply markdown-like formatting\r\n const formattedContent = WorkflowUtils.formatMarkdownLike(escapedContent);\r\n \r\n // Text-Vorschau anzeigen with formatting\r\n previewContent.innerHTML = `\r\n <div class=\"file-preview-text\">${formattedContent}</div>\r\n `;\r\n } catch (contentError) {\r\n console.error(\"Error processing document content:\", contentError);\r\n showFileErrorState(previewContent, \"Fehler beim Formatieren des Dokumenteninhalts\");\r\n }\r\n \r\n return;\r\n } else {\r\n // Check for alternative content locations\r\n if (file.text) {\r\n console.log(\"Found text directly in file object\");\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.formatMarkdownLike(WorkflowUtils.escapeHtml(file.text))}</pre>\r\n `;\r\n return;\r\n } else if (file.source && file.source.contents) {\r\n console.log(\"Found contents in source.contents\");\r\n const sourceContents = file.source.contents;\r\n if (Array.isArray(sourceContents) && sourceContents.length > 0) {\r\n const content = sourceContents[0].text || \"\";\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.formatMarkdownLike(WorkflowUtils.escapeHtml(content))}</pre>\r\n `;\r\n return;\r\n }\r\n } else if (file.content) {\r\n console.log(\"Found content directly in file.content\");\r\n previewContent.innerHTML = `\r\n <pre class=\"file-preview-text\">${WorkflowUtils.formatMarkdownLike(WorkflowUtils.escapeHtml(file.content))}</pre>\r\n `;\r\n return;\r\n }\r\n \r\n // No content found\r\n showFileErrorState(previewContent, \"Generiertes Dokument enthält keine Inhalte\");\r\n return;\r\n }\r\n }\r\n \r\n // Standard file formats from API data\r\n if (mimeType.includes('image')) {\r\n const imageData = `data:${mimeType};base64,${fileData.content}`;\r\n previewContent.innerHTML = `\r\n <img src=\"${imageData}\" alt=\"${file.name}\" class=\"file-preview-image\">\r\n `;\r\n }\r\n else if (mimeType.includes('text') || mimeType.includes('json') || mimeType.includes('csv') || mimeType.includes('markdown')) {\r\n const textContent = fileData.content ? atob(fileData.content) : 'Kein Inhalt verfügbar';\r\n \r\n // Apply markdown formatting for text content\r\n previewContent.innerHTML = `\r\n <div class=\"file-preview-text\">${WorkflowUtils.formatMarkdownLike(WorkflowUtils.escapeHtml(textContent))}</div>\r\n `;\r\n }\r\n else if (mimeType.includes('pdf')) {\r\n const pdfData = `data:${mimeType};base64,${fileData.content}`;\r\n previewContent.innerHTML = `\r\n <embed src=\"${pdfData}\" type=\"application/pdf\" class=\"file-preview-pdf\">\r\n `;\r\n }\r\n else {\r\n previewContent.innerHTML = `\r\n <div class=\"file-info\">\r\n <i class=\"fas ${WorkflowUtils.getFileTypeIcon(file)} fa-3x\"></i>\r\n <h4>${file.name}</h4>\r\n <p>Typ: ${file.content_type || 'Unbekannt'}</p>\r\n <p>Größe: ${WorkflowUtils.formatFileSize(file.size)}</p>\r\n <p class=\"file-preview-unsupported\">Keine Vorschau verfügbar für diesen Dateityp</p>\r\n </div>\r\n `;\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Aktualisiert die Datenstatistik-Anzeige\r\n */\r\nfunction updateDataStatistics(sentBytes, receivedBytes) {\r\n if (!domElements.dataStatisticsEl) return;\r\n \r\n // Datengrößen aktualisieren - Sicherstellen, dass wir mit Zahlen arbeiten\r\n if (sentBytes) dataCounters.sentDataSize += Math.max(0, parseInt(sentBytes, 10) || 0);\r\n if (receivedBytes) dataCounters.receivedDataSize += Math.max(0, parseInt(receivedBytes, 10) || 0);\r\n \r\n // Formatiert die Größe in kB oder MB mit tausender-Trennzeichen\r\n const formatSize = (bytes) => {\r\n if (!bytes || bytes < 0) return '0 B';\r\n if (bytes < 1024) return `${bytes} B`;\r\n \r\n const kbytes = bytes / 1024;\r\n if (kbytes < 1000) return `${Math.round(kbytes)} kB`;\r\n \r\n const mbytes = kbytes / 1024;\r\n return `${Math.round(mbytes * 10) / 10} MB`;\r\n };\r\n \r\n const sentFormatted = formatSize(dataCounters.sentDataSize);\r\n const receivedFormatted = formatSize(dataCounters.receivedDataSize);\r\n \r\n // Aktualisiere die Anzeige mit Pfeilen\r\n domElements.dataStatisticsEl.innerHTML = `\r\n <span class=\"stat-item\">↑ ${sentFormatted}</span>\r\n <span class=\"stat-item\">↓ ${receivedFormatted}</span>\r\n `;\r\n}\r\n\r\n\r\n/**\r\n * Initialize the fixed layout components\r\n */\r\nfunction initializeFixedLayout() {\r\n // Make sure all elements are properly initialized\r\n const container = document.querySelector('.workflow-container');\r\n \r\n if (!container) {\r\n console.warn(\"Workflow container not found, cannot initialize fixed layout\");\r\n return;\r\n }\r\n \r\n // Store initial heights in data attributes\r\n const workflowHeader = document.querySelector('.workflow-header');\r\n \r\n if (workflowHeader && !workflowHeader.getAttribute('data-default-height')) {\r\n const defaultHeight = window.getComputedStyle(workflowHeader).height;\r\n workflowHeader.setAttribute('data-default-height', defaultHeight);\r\n }\r\n \r\n // Set event listeners for window resize to maintain proper layout\r\n window.addEventListener('resize', adjustLayoutOnResize);\r\n \r\n // Initial adjustment\r\n adjustLayoutOnResize();\r\n}\r\n\r\n/**\r\n * Adjust layout dimensions on window resize to ensure proper space allocation\r\n * Updated for the new three-section layout\r\n */\r\nfunction adjustLayoutOnResize() {\r\n const container = document.querySelector('.workflow-container');\r\n const workflowHeader = document.querySelector('.workflow-header');\r\n const chatSection = document.querySelector('.chat-section');\r\n const workflowFooter = document.querySelector('.workflow-footer');\r\n const agentChatMessages = document.getElementById('agent-chat-messages');\r\n \r\n if (!container || !workflowHeader || !chatSection || !workflowFooter) {\r\n console.warn(\"Required layout elements not found for resize adjustment\");\r\n return;\r\n }\r\n \r\n // Calculate available height\r\n const windowHeight = window.innerHeight;\r\n const headerHeight = workflowHeader.offsetHeight;\r\n const footerHeight = workflowFooter.offsetHeight;\r\n \r\n // Calculate remaining height for chat section\r\n const remainingHeight = windowHeight - headerHeight - footerHeight;\r\n \r\n // Set chat section height\r\n if (remainingHeight > 100) { // Ensure minimum height\r\n chatSection.style.height = `${remainingHeight}px`;\r\n \r\n // If chat messages element exists, make sure it has appropriate height\r\n if (agentChatMessages) {\r\n // Get height of any potential headers within the chat area\r\n const chatHeader = document.querySelector('.chat-area-header');\r\n const chatHeaderHeight = chatHeader ? chatHeader.offsetHeight : 0;\r\n \r\n const messagesHeight = remainingHeight - chatHeaderHeight;\r\n if (messagesHeight > 0) {\r\n agentChatMessages.style.height = `${messagesHeight}px`;\r\n }\r\n }\r\n }\r\n \r\n // Ensure file preview section has appropriate height\r\n const filePreviewContainer = document.getElementById('file-preview-container');\r\n if (filePreviewContainer) {\r\n filePreviewContainer.style.height = `${remainingHeight}px`;\r\n \r\n // Adjust content area height to account for header\r\n const filePreviewHeader = filePreviewContainer.querySelector('.file-preview-header');\r\n const filePreviewContent = document.getElementById('file-preview-content');\r\n \r\n if (filePreviewHeader && filePreviewContent) {\r\n const headerHeight = filePreviewHeader.offsetHeight;\r\n filePreviewContent.style.height = `${remainingHeight - headerHeight}px`;\r\n }\r\n }\r\n \r\n console.log(\"Layout angepasst: Window Height\", windowHeight, \r\n \"Header\", headerHeight, \r\n \"Footer\", footerHeight, \r\n \"Chat Section\", remainingHeight);\r\n}\r\n\r\n\r\n/**\r\n * Handler für Layout-Änderungen\r\n * @param {Object} layoutConfig - Konfigurationsobjekt mit Layout-Änderungen\r\n */\r\nfunction handleLayoutChange(layoutConfig) {\r\n // Hier Logik für Layout-Änderungen implementieren\r\n console.log(\"Layout-Änderung:\", layoutConfig);\r\n}\r\n\r\n/**\r\n * Zeigt einen Toast-Benachrichtigung an\r\n */\r\nfunction showToast(title, message, type = 'success') {\r\n // Toast-Container erstellen, wenn er nicht existiert\r\n let toastContainer = document.getElementById('toast-container');\r\n if (!toastContainer) {\r\n toastContainer = document.createElement('div');\r\n toastContainer.id = 'toast-container';\r\n document.body.appendChild(toastContainer);\r\n }\r\n \r\n // Toast-Element erstellen\r\n const toast = document.createElement('div');\r\n toast.className = `toast ${type}`;\r\n toast.innerHTML = `\r\n <div class=\"toast-content\">\r\n <i class=\"fas ${type === 'success' ? 'fa-check-circle' : \r\n type === 'error' ? 'fa-exclamation-circle' : \r\n type === 'warning' ? 'fa-exclamation-triangle' : \r\n 'fa-info-circle'}\"></i>\r\n <div class=\"toast-message\">\r\n <div class=\"toast-title\">${title}</div>\r\n <div class=\"toast-details\">${message}</div>\r\n </div>\r\n </div>\r\n `;\r\n \r\n // Zum Container hinzufügen\r\n toastContainer.appendChild(toast);\r\n \r\n // Nach Verzögerung entfernen\r\n setTimeout(() => {\r\n toast.classList.add('hide');\r\n setTimeout(() => {\r\n if (toast.parentNode) {\r\n toast.parentNode.removeChild(toast);\r\n }\r\n }, 300);\r\n }, 3000);\r\n}\r\n\r\n// Exportierte Funktionen\r\nexport {\r\n initUI,\r\n renderLogs,\r\n renderChatMessages,\r\n updateWorkflowButtonsState,\r\n updateDataStatistics,\r\n previewFile,\r\n handleLayoutChange,\r\n renderAdditionalFiles,\r\n updatePromptVisualization,\r\n updateChatMessageFiles,\r\n showToast\r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "doc_bff4d8d8-b18a-497a-a8f4-62e9da460571",
|
|
"source": {
|
|
"type": "file",
|
|
"id": "file_48e39fd7-8a41-4a5e-be66-15579dfa0cd2",
|
|
"name": "workflow_features.js",
|
|
"content_type": "application/javascript",
|
|
"size": 33460,
|
|
"upload_date": "2025-04-15T17:13:30.170889"
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "/**\r\n * workflow_features.js\r\n * Feature-Modul für das Workflow-System mit Chat-Funktionalität, \r\n * Dateiverarbeitung, Log-Management und Benutzereingabe\r\n */\r\n\r\nimport * as WorkflowUI from './workflow_ui.js';\r\nimport * as WorkflowData from './workflow_data.js';\r\nimport * as WorkflowUtils from './workflow_utils.js';\r\nimport * as WorkflowCoordination from './workflow_coordination.js';\r\n\r\n// Referenz auf den Workflow-Zustand\r\nlet workflowState = null;\r\n\r\n// Callbacks\r\nlet callbacks = {\r\n onWorkflowStarted: null,\r\n onUserResponseSent: null,\r\n onFilePreview: null\r\n};\r\n\r\n/**\r\n * Initialisiert das Feature-Modul\r\n * @param {Object} workflowStateObj - Gemeinsam genutzter Workflow-Zustand\r\n * @param {Object} callbackFunctions - Callback-Funktionen\r\n */\r\nfunction initFeatures(workflowStateObj, callbackFunctions) {\r\n console.log(\"Initialisiere Workflow-Features...\");\r\n \r\n try {\r\n // Workflow-Zustand speichern\r\n workflowState = workflowStateObj;\r\n \r\n if (!workflowState) {\r\n throw new Error(\"Ungültiger Workflow-Zustand\");\r\n }\r\n \r\n // Callbacks setzen mit Fehlerbehandlung\r\n callbacks = {\r\n onWorkflowStarted: callbackFunctions.onWorkflowStarted || (() => {\r\n console.warn(\"onWorkflowStarted Callback nicht definiert\");\r\n }),\r\n onUserResponseSent: callbackFunctions.onUserResponseSent || (() => {\r\n console.warn(\"onUserResponseSent Callback nicht definiert\");\r\n }),\r\n onFilePreview: callbackFunctions.onFilePreview || (() => {\r\n console.warn(\"onFilePreview Callback nicht definiert\");\r\n })\r\n };\r\n \r\n // DOM-Elemente initialisieren - this will now initialize Coordination's userInputState\r\n initializeDomElements();\r\n \r\n // Event-Listener einrichten\r\n setupEventListeners();\r\n \r\n // Drag & Drop initialisieren\r\n initializeDragAndDrop();\r\n \r\n // Prompt-Optionen laden\r\n loadPromptOptions();\r\n \r\n // Mit initialem Prompt-View starten - Use coordination function\r\n WorkflowCoordination.showInitialPromptView();\r\n \r\n console.log(\"Workflow-Features erfolgreich initialisiert\");\r\n } catch (error) {\r\n console.error(\"Fehler bei der Initialisierung von Workflow-Features:\", error);\r\n \r\n // Auf UI Fehler anzeigen\r\n if (document.body) {\r\n const errorElement = document.createElement('div');\r\n errorElement.className = 'workflow-error-message';\r\n errorElement.textContent = `Fehler bei der Initialisierung: ${error.message}`;\r\n \r\n // Versuche, den Workflow-Container zu finden\r\n const container = document.querySelector('.workflow-container');\r\n if (container) {\r\n container.appendChild(errorElement);\r\n } else {\r\n // Fallback: Füge zum Body hinzu\r\n document.body.appendChild(errorElement);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Initialisiert alle DOM-Elementreferenzen\r\n */\r\nfunction initializeDomElements() {\r\n const elements = WorkflowCoordination.userInputState.domElements;\r\n \r\n console.log(\"Initialisiere DOM-Elemente für Workflow-Features...\");\r\n \r\n // Primary UI elements (required)\r\n elements.userInputArea = document.getElementById('user-input-area');\r\n elements.userMessageInput = document.getElementById('user-message-input');\r\n elements.sendUserMessageBtn = document.getElementById('send-user-message-btn');\r\n elements.stopWorkflowBtn = document.getElementById('stop-workflow-btn');\r\n elements.resetBtn = document.getElementById('reset-btn');\r\n\r\n // Prompt selection\r\n elements.promptSelectMain = document.getElementById('prompt-select-main');\r\n \r\n // File upload elements\r\n elements.uploadAdditionalFileBtn = document.getElementById('upload-additional-file-btn');\r\n elements.additionalFileInput = document.getElementById('additional-file-input');\r\n elements.additionalFilesContainer = document.getElementById('additional-files-container');\r\n \r\n // Log and chat message containers\r\n elements.executionLog = document.getElementById('execution-log');\r\n elements.agentChatMessages = document.getElementById('agent-chat-messages');\r\n elements.emptyChatState = document.getElementById('empty-chat-state');\r\n \r\n // Optional: file preview elements\r\n elements.filePreviewContainer = document.getElementById('file-preview-container');\r\n elements.filePreviewContent = document.getElementById('file-preview-content');\r\n elements.downloadFileBtn = document.getElementById('download-file-btn');\r\n elements.copyFileBtn = document.getElementById('copy-file-btn');\r\n \r\n // Layout elements for the 3-column structure\r\n elements.workflowHeader = document.querySelector('.workflow-header');\r\n elements.chatSection = document.querySelector('.chat-section');\r\n elements.workflowFooter = document.querySelector('.workflow-footer');\r\n elements.headerLeft = document.querySelector('.header-left');\r\n elements.headerRight = document.querySelector('.header-right');\r\n elements.chatLeft = document.querySelector('.chat-left');\r\n elements.chatRight = document.querySelector('.chat-right');\r\n elements.footerLeft = document.querySelector('.footer-left');\r\n elements.footerRight = document.querySelector('.footer-right');\r\n \r\n // Debug-Logging\r\n const foundElements = Object.keys(elements).filter(key => elements[key] !== null).length;\r\n const missingElements = Object.keys(elements).filter(key => elements[key] === null);\r\n \r\n console.log(`DOM-Elemente initialisiert: ${foundElements} gefunden, ${missingElements.length} fehlen`);\r\n \r\n if (missingElements.length > 0) {\r\n console.warn(\"Fehlende DOM-Elemente:\", missingElements.join(', '));\r\n }\r\n \r\n // Validate required elements\r\n validateRequiredElements();\r\n}\r\n\r\n\r\n/**\r\n * Prüft, ob alle erforderlichen DOM-Elemente vorhanden sind\r\n */\r\nfunction validateRequiredElements() {\r\n const elements = WorkflowCoordination.userInputState.domElements;\r\n const requiredElements = [\r\n 'userInputArea',\r\n 'userMessageInput',\r\n 'sendUserMessageBtn',\r\n 'promptSelectMain',\r\n 'uploadAdditionalFileBtn',\r\n 'additionalFileInput',\r\n 'additionalFilesContainer',\r\n // Diese Elemente sind besonders wichtig für die UI-Aktualisierung\r\n 'executionLog',\r\n 'agentChatMessages'\r\n ];\r\n \r\n let missingElements = [];\r\n \r\n requiredElements.forEach(elementKey => {\r\n if (!elements[elementKey]) {\r\n console.error(`Erforderliches DOM-Element '${elementKey}' nicht gefunden. Workflow-Funktionalität wird eingeschränkt sein.`);\r\n missingElements.push(elementKey);\r\n }\r\n });\r\n \r\n if (missingElements.length > 0) {\r\n console.error(`Fehlende DOM-Elemente: ${missingElements.join(', ')}`);\r\n \r\n // Versuche alternative Selektoren\r\n missingElements.forEach(elementKey => {\r\n // Konvertiere camelCase zu kebab-case für alternative IDs\r\n const alternativeId = elementKey\r\n .replace(/([A-Z])/g, '-$1')\r\n .toLowerCase();\r\n \r\n console.log(`Versuche alternatives Element mit ID '${alternativeId}' zu finden`);\r\n const alternativeElement = document.getElementById(alternativeId);\r\n \r\n if (alternativeElement) {\r\n console.log(`Alternatives Element mit ID '${alternativeId}' gefunden. Verwende dieses.`);\r\n elements[elementKey] = alternativeElement;\r\n } else {\r\n // Versuche, Element durch Klassenname zu finden\r\n const alternativeClass = `.${alternativeId}`;\r\n const elementByClass = document.querySelector(alternativeClass);\r\n \r\n if (elementByClass) {\r\n console.log(`Element mit Klasse '${alternativeClass}' gefunden. Verwende dieses.`);\r\n elements[elementKey] = elementByClass;\r\n }\r\n }\r\n });\r\n \r\n // Überprüfe, ob wir kritische Elemente jetzt haben\r\n const stillMissing = requiredElements.filter(key => !elements[key]);\r\n if (stillMissing.length > 0) {\r\n console.warn(`Immer noch fehlende kritische Elemente: ${stillMissing.join(', ')}`);\r\n return false;\r\n }\r\n }\r\n \r\n console.log(\"Alle erforderlichen DOM-Elemente gefunden\");\r\n return true;\r\n}\r\n\r\n\r\n/**\r\n * Richtet alle Event-Listener ein\r\n */\r\nfunction setupEventListeners() {\r\n const elements = WorkflowCoordination.userInputState.domElements;\r\n \r\n console.log(\"Event-Listener werden eingerichtet...\");\r\n \r\n // Prompt Selection\r\n if (elements.promptSelectMain) {\r\n console.log(\"Event-Listener für Prompt-Auswahl eingerichtet\");\r\n elements.promptSelectMain.addEventListener('change', handlePromptSelection);\r\n } else {\r\n console.warn(\"Prompt-Auswahl-Element nicht gefunden. Event-Listener kann nicht eingerichtet werden.\");\r\n }\r\n \r\n // User Message Input\r\n if (elements.userMessageInput) {\r\n console.log(\"Event-Listener für Nachrichteneingabe eingerichtet\");\r\n \r\n // Input-Event für Textverfolgung\r\n elements.userMessageInput.addEventListener('input', (e) => {\r\n WorkflowCoordination.userInputState.promptText = e.target.value;\r\n });\r\n \r\n // Enter-Taste für Absenden\r\n elements.userMessageInput.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n console.log(\"Enter-Taste gedrückt. Sende Benutzerantwort.\");\r\n sendUserResponse();\r\n }\r\n });\r\n } else {\r\n console.warn(\"Nachrichteneingabe-Element nicht gefunden. Event-Listener kann nicht eingerichtet werden.\");\r\n }\r\n \r\n // Send User Message Button\r\n if (elements.sendUserMessageBtn) {\r\n console.log(\"Event-Listener für Senden-Button eingerichtet\");\r\n \r\n // WICHTIG: Alle existierenden Event-Listener entfernen, falls es Duplikate gibt\r\n const newButton = elements.sendUserMessageBtn.cloneNode(true);\r\n elements.sendUserMessageBtn.parentNode.replaceChild(newButton, elements.sendUserMessageBtn);\r\n elements.sendUserMessageBtn = newButton;\r\n \r\n newButton.addEventListener('click', (e) => {\r\n console.log(\"Senden-Button geklickt. Sende Benutzerantwort.\");\r\n sendUserResponse();\r\n });\r\n } else {\r\n console.warn(\"Senden-Button nicht gefunden. Event-Listener kann nicht eingerichtet werden.\");\r\n }\r\n \r\n // Additional File Upload\r\n if (elements.uploadAdditionalFileBtn && elements.additionalFileInput) {\r\n console.log(\"Event-Listener für Datei-Upload eingerichtet\");\r\n \r\n elements.uploadAdditionalFileBtn.addEventListener('click', () => {\r\n elements.additionalFileInput.click();\r\n });\r\n \r\n elements.additionalFileInput.addEventListener('change', (event) => {\r\n const files = event.target.files;\r\n console.log(`${files.length} Dateien für Upload ausgewählt`);\r\n \r\n for (let file of files) {\r\n WorkflowData.uploadAndAddFile(file, false);\r\n }\r\n event.target.value = '';\r\n });\r\n } else {\r\n console.warn(\"Datei-Upload-Elemente nicht gefunden. Event-Listener kann nicht eingerichtet werden.\");\r\n }\r\n \r\n // Additional Files Container (for file removal)\r\n if (elements.additionalFilesContainer) {\r\n console.log(\"Event-Listener für angehängte Dateien eingerichtet\");\r\n \r\n elements.additionalFilesContainer.addEventListener('click', (e) => {\r\n const removeBtn = e.target.closest('.remove-additional-file');\r\n if (removeBtn) {\r\n const index = parseInt(removeBtn.getAttribute('data-index'), 10);\r\n if (!isNaN(index) && index >= 0 && index < WorkflowCoordination.userInputState.additionalFiles.length) {\r\n console.log(`Entferne Datei an Index ${index}`);\r\n WorkflowCoordination.userInputState.additionalFiles.splice(index, 1);\r\n WorkflowUI.renderAdditionalFiles();\r\n WorkflowUI.updatePromptVisualization();\r\n }\r\n }\r\n });\r\n } else {\r\n console.warn(\"Container für angehängte Dateien nicht gefunden. Event-Listener kann nicht eingerichtet werden.\");\r\n }\r\n \r\n console.log(\"Event-Listener-Setup abgeschlossen\");\r\n}\r\n\r\n\r\n/**\r\n * Initialisiert Drag & Drop für Dateien\r\n */\r\nfunction initializeDragAndDrop() {\r\n // Direkter Zugriff oder mit Fallback über DOM-Selektor\r\n const userInputArea = WorkflowCoordination.userInputState.domElements.userInputArea || document.getElementById('user-input-area');\r\n \r\n if (!userInputArea) {\r\n console.warn(\"User input area not found for drag & drop\");\r\n return;\r\n }\r\n\r\n console.log(\"Initialisiere Drag & Drop für Dateien auf Element:\", userInputArea);\r\n\r\n // Prevent default drag behaviors\r\n ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {\r\n userInputArea.addEventListener(eventName, preventDefaults, false);\r\n });\r\n\r\n userInputArea.addEventListener('dragenter', highlight, false);\r\n userInputArea.addEventListener('dragover', highlight, false);\r\n userInputArea.addEventListener('dragleave', unhighlight, false);\r\n userInputArea.addEventListener('drop', handleDrop, false);\r\n\r\n function preventDefaults(e) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }\r\n\r\n function highlight() {\r\n userInputArea.classList.add('dragging');\r\n }\r\n\r\n function unhighlight() {\r\n userInputArea.classList.remove('dragging');\r\n }\r\n\r\n function handleDrop(e) {\r\n unhighlight();\r\n const files = e.dataTransfer.files;\r\n \r\n console.log(`${files.length} Dateien wurden gedroppt`);\r\n \r\n if (files.length > 0) {\r\n // Always use additionalFiles for dropped files\r\n for (let file of files) {\r\n WorkflowData.uploadAndAddFile(file, false);\r\n }\r\n }\r\n }\r\n \r\n console.log(\"Drag & Drop für Dateien erfolgreich initialisiert\");\r\n}\r\n\r\n\r\n/**\r\n * Handler für 'User Response Sent' Event mit Polling-Neustart\r\n */\r\nfunction handleUserResponseSent(workflowId, message, additionalFileIds) {\r\n console.log(`Benutzerantwort gesendet für Workflow ${workflowId}`);\r\n \r\n // CRITICAL FIX: Ensure proper state\r\n workflowState.isWorkflowComplete = false;\r\n workflowState.isRunning = true;\r\n workflowState.finalPollingCycle = false;\r\n \r\n // Add log entry\r\n WorkflowCoordination.addLogEntry(`Benutzer hat geantwortet: ${message.substring(0, 30)}${message.length > 30 ? '...' : ''}`, 'info', null, 'User Agent');\r\n \r\n if (additionalFileIds && additionalFileIds.length > 0) {\r\n WorkflowCoordination.addLogEntry(`${additionalFileIds.length} zusätzliche Dateien hochgeladen`, 'info', null, 'User Agent');\r\n }\r\n\r\n // Dateiobjekte für die gegebenen Datei-IDs suchen\r\n const additionalFiles = getFilesById(additionalFileIds);\r\n \r\n // Erstelle Dokumente-Array für die Benutzernachricht\r\n const documents = additionalFiles.map(file => ({\r\n id: `doc_${file.id}_${Date.now()}`,\r\n source: {\r\n type: 'file',\r\n id: file.id,\r\n name: file.name,\r\n content_type: file.content_type,\r\n size: file.size\r\n },\r\n contents: [{\r\n type: 'text',\r\n text: `File: ${file.name} (Type: ${file.content_type || 'unknown'})`\r\n }]\r\n }));\r\n \r\n // Benutzernachricht zum Chat hinzufügen\r\n WorkflowCoordination.addChatMessage({\r\n id: `msg_user_${Date.now()}`,\r\n type: 'agent',\r\n agent_id: 'user_agent',\r\n agent_name: 'User Agent',\r\n content: message,\r\n timestamp: new Date().toISOString(),\r\n documents: documents\r\n });\r\n \r\n // System-Nachricht hinzufügen\r\n WorkflowCoordination.addChatMessage({\r\n id: `msg_system_${Date.now()}`,\r\n type: 'system',\r\n content: 'Moderator analysiert die Anfrage und wählt passende Agenten aus...',\r\n timestamp: new Date().toISOString()\r\n });\r\n \r\n // UI aktualisieren\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // WICHTIG: Polling NEUSTARTEN nachdem Benutzer geantwortet hat\r\n console.log(\"Polling wird nach Benutzereingabe neu gestartet\");\r\n WorkflowCoordination.addLogEntry(\"Workflow wird mit Benutzereingabe fortgesetzt\", \"info\");\r\n \r\n // Klarer Restart des Pollings mit kleiner Verzögerung\r\n setTimeout(() => {\r\n console.log(\"Starte Polling nach kurzer Verzögerung\");\r\n WorkflowData.pollWorkflowStatus(workflowId);\r\n }, 500);\r\n}\r\n\r\n\r\n/**\r\n * Function zum Laden von Prompts und Befüllen des Dropdown\r\n */\r\nfunction loadPromptOptions() {\r\n const selectElement = document.getElementById('prompt-select-main');\r\n if (!selectElement || !window.globalState || !window.globalState.mainView) {\r\n console.warn(\"Kann Prompts nicht laden: Fehlende Elemente oder globaler Zustand\");\r\n return;\r\n }\r\n \r\n // Clear existing options, keeping the default\r\n while (selectElement.options.length > 1) {\r\n selectElement.remove(1);\r\n }\r\n \r\n const prompts = window.globalState.mainView.availablePrompts || [];\r\n \r\n prompts.forEach(prompt => {\r\n const option = document.createElement('option');\r\n option.value = prompt.id;\r\n option.textContent = prompt.name || `Prompt ${prompt.id}`;\r\n selectElement.appendChild(option);\r\n });\r\n}\r\n\r\n\r\n/**\r\n * Verarbeitet Auswahl aus dem Prompt-Dropdown\r\n */\r\nfunction handlePromptSelection() {\r\n const selectElement = document.getElementById('prompt-select-main');\r\n const messageInput = document.getElementById('user-message-input');\r\n \r\n if (!selectElement || !messageInput) return;\r\n \r\n const selectedPromptId = selectElement.value;\r\n \r\n if (!selectedPromptId) {\r\n messageInput.value = '';\r\n return;\r\n }\r\n \r\n const prompts = window.globalState.mainView.availablePrompts || [];\r\n const selectedPrompt = prompts.find(p => String(p.id) === selectedPromptId);\r\n \r\n if (selectedPrompt) {\r\n messageInput.value = selectedPrompt.content;\r\n messageInput.focus();\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Sendet eine Benutzerantwort an den laufenden Workflow\r\n * Updated for the new backend architecture\r\n */\r\nasync function sendUserResponse() {\r\n const elements = WorkflowCoordination.userInputState.domElements;\r\n \r\n console.log(\"sendUserResponse aufgerufen:\", { \r\n isInitialPrompt: WorkflowCoordination.userInputState.isInitialPrompt, \r\n activeWorkflowId: WorkflowCoordination.userInputState.activeWorkflowId \r\n });\r\n \r\n if (!elements.userMessageInput) {\r\n console.error(\"Fehler: userMessageInput nicht gefunden\");\r\n WorkflowUtils.showError(\"Fehler\", \"Keine Eingabe vorhanden.\");\r\n return;\r\n }\r\n \r\n const userMessage = WorkflowCoordination.userInputState.promptText.trim();\r\n if (!userMessage) {\r\n console.warn(\"Leere Benutzereingabe\");\r\n WorkflowUtils.showError(\"Fehlende Eingabe\", \"Bitte geben Sie eine Nachricht ein.\");\r\n return;\r\n }\r\n \r\n // UI während der Verarbeitung sperren\r\n WorkflowCoordination.setLoadingState(true);\r\n \r\n try {\r\n // Sowohl die initiale als auch die laufende Workflow-Behandlung kombinieren\r\n if (!WorkflowCoordination.userInputState.activeWorkflowId) {\r\n // Dies ist ein initialer Prompt - einen neuen Workflow starten\r\n console.log(`Erstelle neuen Workflow mit Prompt: \"${userMessage.substring(0, 50)}${userMessage.length > 50 ? '...' : ''}\"`);\r\n \r\n // Beide Arrays für Dateien kombinieren\r\n let filesToInclude = [...WorkflowCoordination.userInputState.additionalFiles]; \r\n\r\n if (WorkflowCoordination.userInputState.additionalFiles.length > 0) {\r\n console.log(`Füge ${WorkflowCoordination.userInputState.additionalFiles.length} zusätzliche Dateien zum neuen Workflow hinzu`);\r\n }\r\n \r\n try {\r\n // Workflow über WorkflowData-Modul erstellen\r\n const response = await WorkflowData.createWorkflow(userMessage, filesToInclude);\r\n \r\n if (response && response.workflow_id) {\r\n // Aktiven Workflow setzen\r\n WorkflowCoordination.setActiveWorkflow(response.workflow_id);\r\n \r\n // UI-Elemente aktualisieren für bessere Sichtbarkeit\r\n if (elements.emptyChatState) {\r\n elements.emptyChatState.style.display = 'none';\r\n }\r\n \r\n if (elements.agentChatMessages) {\r\n elements.agentChatMessages.style.display = 'block';\r\n }\r\n \r\n // Callback aufrufen, falls vorhanden\r\n if (callbacks.onWorkflowStarted) {\r\n console.log(\"Rufe onWorkflowStarted Callback mit Workflow-ID auf:\", response.workflow_id);\r\n callbacks.onWorkflowStarted(response.workflow_id, {\r\n prompt: userMessage,\r\n files: filesToInclude\r\n });\r\n } else {\r\n console.error(\"onWorkflowStarted Callback nicht verfügbar\");\r\n }\r\n \r\n console.log(`Workflow gestartet mit ID: ${response.workflow_id}`);\r\n } else {\r\n console.error(\"Workflow-Erstellung fehlgeschlagen:\", response);\r\n throw new Error(\"Workflow konnte nicht gestartet werden. Keine Workflow-ID erhalten.\");\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Erstellen des Workflows:\", error);\r\n throw error;\r\n }\r\n } \r\n else {\r\n // Dies ist eine Antwort auf einen laufenden Workflow\r\n console.log(`Sende Antwort an Workflow ${WorkflowCoordination.userInputState.activeWorkflowId}`);\r\n \r\n // Zusätzliche Dateien erfassen\r\n const additionalFileIds = WorkflowCoordination.userInputState.additionalFiles.map(file => file.id);\r\n \r\n try {\r\n // Benutzereingabe senden - Make sure we're using the right function\r\n const response = await WorkflowData.submitUserInput(\r\n WorkflowCoordination.userInputState.activeWorkflowId,\r\n userMessage,\r\n additionalFileIds\r\n );\r\n \r\n console.log('Benutzerantwort erfolgreich gesendet:', response);\r\n \r\n // Mark workflow as running again if it was completed\r\n workflowState.isWorkflowComplete = false;\r\n workflowState.isRunning = true;\r\n workflowState.finalPollingCycle = false;\r\n \r\n // Update UI to reflect running state\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // Restart polling\r\n WorkflowData.pollWorkflowStatus(WorkflowCoordination.userInputState.activeWorkflowId);\r\n \r\n // Callback aufrufen, falls vorhanden\r\n if (callbacks.onUserResponseSent) {\r\n console.log(\"Rufe onUserResponseSent Callback auf\");\r\n callbacks.onUserResponseSent(WorkflowCoordination.userInputState.activeWorkflowId, userMessage, additionalFileIds);\r\n } else {\r\n // Fallback if callback not available\r\n console.log(\"Callback nicht verfügbar, starte Polling direkt\");\r\n workflowState.isWorkflowComplete = false;\r\n workflowState.isRunning = true;\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n WorkflowData.pollWorkflowStatus(WorkflowCoordination.userInputState.activeWorkflowId);\r\n } \r\n // Dateien zurücksetzen\r\n WorkflowCoordination.userInputState.additionalFiles = [];\r\n WorkflowUI.renderAdditionalFiles();\r\n } catch (error) {\r\n console.error(\"Fehler beim Senden der Benutzerantwort:\", error);\r\n throw error;\r\n }\r\n }\r\n \r\n // Eingabe zurücksetzen\r\n elements.userMessageInput.value = '';\r\n WorkflowCoordination.userInputState.promptText = '';\r\n WorkflowCoordination.clearAttachedFiles();\r\n \r\n } catch (error) {\r\n console.error(\"Fehler beim Senden der Nachricht:\", error);\r\n WorkflowUtils.showError(\"Kommunikationsfehler\", `Die Nachricht konnte nicht gesendet werden: ${error.message}`);\r\n } finally {\r\n // UI entsperren\r\n WorkflowCoordination.setLoadingState(false);\r\n }\r\n}\r\n\r\n/**\r\n * Gibt die ausgewählten Dateien zurück (je nach Kontext)\r\n * @returns {Array} - Die ausgewählten Dateien\r\n */\r\nfunction getSelectedFiles() {\r\n return WorkflowCoordination.userInputState.additionalFiles;\r\n}\r\n\r\n\r\n/**\r\n * Sucht Dateiobjekte anhand ihrer IDs\r\n * Enhanced to search for files with partial ID matches for better compatibility\r\n * @param {Array} fileIds - Array mit Datei-IDs\r\n * @returns {Array} - Array mit gefundenen Dateiobjekten\r\n */\r\nfunction getFilesById(fileIds) {\r\n if (!fileIds || !Array.isArray(fileIds) || fileIds.length === 0) return [];\r\n \r\n const result = [];\r\n const processedIds = new Set(); // Für Deduplizierung\r\n \r\n // In globalState.mainView.availableFiles suchen\r\n if (globalState && globalState.mainView && globalState.mainView.availableFiles) {\r\n fileIds.forEach(fileId => {\r\n // Sicherstellen, dass die ID gültig ist und nicht bereits verarbeitet wurde\r\n if (!fileId || processedIds.has(fileId)) return;\r\n \r\n // First try exact match\r\n let file = globalState.mainView.availableFiles.find(f => f.id === fileId);\r\n \r\n // If not found, try partial match (file ID might be part of a longer ID)\r\n if (!file) {\r\n file = globalState.mainView.availableFiles.find(f => \r\n (typeof f.id === 'string' && typeof fileId === 'string' && \r\n (f.id.includes(fileId) || fileId.includes(f.id)))\r\n );\r\n }\r\n \r\n if (file) {\r\n result.push(file);\r\n processedIds.add(fileId);\r\n } else {\r\n console.warn(`Datei mit ID ${fileId} nicht in globalState.mainView.availableFiles gefunden`);\r\n }\r\n });\r\n } else {\r\n console.warn(\"globalState.mainView.availableFiles nicht verfügbar\");\r\n }\r\n \r\n if (result.length < fileIds.length) {\r\n console.warn(`Nicht alle angeforderten Dateien gefunden: ${result.length} von ${fileIds.length}`);\r\n }\r\n \r\n return result;\r\n}\r\n\r\n\r\n/**\r\n * Handler für 'Workflow gestartet' Event\r\n * Updated to handle new workflow structure\r\n */\r\nfunction handleWorkflowStarted(workflowId, workflowData) {\r\n console.log(`Workflow gestartet mit ID: ${workflowId}`);\r\n \r\n // Workflow-Zustand aktualisieren\r\n workflowState.currentWorkflowId = workflowId;\r\n workflowState.isRunning = true;\r\n workflowState.isStoppingWorkflow = false;\r\n workflowState.logs = [];\r\n workflowState.chatMessages = [];\r\n workflowState.lastPolledLogId = null;\r\n workflowState.lastPolledMessageId = null;\r\n workflowState.isWorkflowComplete = false;\r\n workflowState.finalPollingCycle = false; // Reset final polling state\r\n\r\n // Clear the file attachments in the UI\r\n if (WorkflowCoordination && typeof WorkflowCoordination.clearAttachedFiles === 'function') {\r\n WorkflowCoordination.clearAttachedFiles();\r\n }\r\n \r\n // UI aktualisieren\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // Log-Eintrag hinzufügen\r\n WorkflowCoordination.addLogEntry(\"Workflow wird gestartet...\", \"info\");\r\n \r\n // System-Nachricht zum Chat hinzufügen\r\n WorkflowCoordination.addChatMessage({\r\n id: `msg_system_${Date.now()}`,\r\n type: 'system',\r\n content: 'Multi-Agent Chat wurde gestartet',\r\n timestamp: new Date().toISOString()\r\n });\r\n \r\n // Polling für Workflow-Status starten\r\n WorkflowData.pollWorkflowStatus(workflowId);\r\n}\r\n\r\n\r\n/**\r\n * Workflow stoppen\r\n * Updated for the refactored backend\r\n */\r\nasync function stopWorkflow() {\r\n if (!workflowState.isRunning || !workflowState.currentWorkflowId) {\r\n console.warn(\"Kein laufender Workflow zum Stoppen vorhanden\");\r\n return;\r\n }\r\n \r\n if (workflowState.isStoppingWorkflow) {\r\n console.warn(\"Workflow wird bereits gestoppt\");\r\n return;\r\n }\r\n \r\n try {\r\n // UI-Status aktualisieren\r\n workflowState.isStoppingWorkflow = true;\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // Log-Eintrag hinzufügen\r\n WorkflowCoordination.addLogEntry(\"Workflow wird gestoppt...\", \"warning\");\r\n \r\n // Backend aufrufen, um den Workflow zu stoppen\r\n const response = await WorkflowData.stopWorkflow(workflowState.currentWorkflowId);\r\n \r\n if (response && (response.status === \"stopped\" || response.message === \"Workflow wurde gestoppt\")) {\r\n WorkflowCoordination.addLogEntry(\"Workflow wurde manuell gestoppt\", \"warning\");\r\n \r\n // Chat-Nachricht über Abbruch hinzufügen\r\n WorkflowCoordination.addChatMessage({\r\n type: 'system',\r\n content: 'Multi-Agent Chat wurde manuell gestoppt',\r\n timestamp: new Date().toISOString()\r\n });\r\n\r\n // Workflow als beendet markieren\r\n workflowState.isRunning = false;\r\n workflowState.isWorkflowComplete = true;\r\n \r\n // UI aktualisieren\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n \r\n // Letzte Daten einmalig abrufen ohne neues Polling zu starten\r\n try {\r\n // Letzte Logs und Chat-Nachrichten abrufen\r\n await WorkflowData.pollWorkflowLogs(workflowState.currentWorkflowId);\r\n await WorkflowData.pollWorkflowMessages(workflowState.currentWorkflowId);\r\n } catch (innerError) {\r\n console.error(\"Fehler beim Abrufen der letzten Daten:\", innerError);\r\n }\r\n \r\n // Reset UI for new workflow input\r\n WorkflowCoordination.resetUserInput();\r\n WorkflowCoordination.showInitialPromptView();\r\n }\r\n } catch (error) {\r\n console.error(\"Fehler beim Stoppen des Workflows:\", error);\r\n WorkflowCoordination.addLogEntry(`Fehler beim Stoppen des Workflows: ${error.message}`, \"error\");\r\n } finally {\r\n // Sicherstellen, dass der Stopp-Status zurückgesetzt wird\r\n workflowState.isStoppingWorkflow = false;\r\n workflowState.isRunning = false;\r\n WorkflowUI.updateWorkflowButtonsState(workflowState);\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Workflow vollständig zurücksetzen\r\n */\r\nfunction resetWorkflow() {\r\n if (workflowState.isRunning) {\r\n WorkflowUtils.showConfirm(\r\n \"Workflow läuft noch. Möchten Sie wirklich zurücksetzen?\",\r\n () => {\r\n // Alles zurücksetzen\r\n performReset();\r\n }\r\n );\r\n } else {\r\n performReset();\r\n }\r\n}\r\n\r\n/**\r\n * Führt das eigentliche Zurücksetzen durch\r\n */\r\nfunction performReset() {\r\n workflowState.logs = [];\r\n workflowState.chatMessages = [];\r\n workflowState.isRunning = false;\r\n workflowState.currentWorkflowId = null;\r\n workflowState.lastPolledLogId = null;\r\n workflowState.lastPolledMessageId = null;\r\n workflowState.waitingForUserInput = false;\r\n \r\n // Listen neu rendern\r\n WorkflowUI.renderLogs(workflowState.logs);\r\n WorkflowUI.renderChatMessages(workflowState.chatMessages, workflowState.currentWorkflowId);\r\n WorkflowUI.updateDataStatistics(0, 0);\r\n \r\n // Benutzereingabe zurücksetzen\r\n WorkflowCoordination.resetUserInput();\r\n WorkflowCoordination.showInitialPromptView();\r\n \r\n // Button zurücksetzen\r\n WorkflowCoordination.finishWorkflow();\r\n}\r\n\r\n// Exportierte Funktionen\r\nexport {\r\n initFeatures,\r\n resetWorkflow,\r\n stopWorkflow,\r\n handleUserResponseSent,\r\n handleWorkflowStarted,\r\n sendUserResponse,\r\n getFilesById \r\n};",
|
|
"is_extracted": true,
|
|
"extraction_context": null
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"content": "Bitte beschreibe mir das Frontend gemäss dem beiliegenden Code: Was ist das , wie funktioniert es und was kann ich damit an Mehrwert erreichen",
|
|
"agent_type": null
|
|
},
|
|
{
|
|
"id": "msg_dedd887d-42b2-4b3b-9cbf-862bae784648",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:14:03.761169",
|
|
"finished_at": null,
|
|
"sequence_no": 2,
|
|
"status": "pending",
|
|
"role": "assistant",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [],
|
|
"content": "# Statistical Analysis\n\nError generating analysis: 500: Fehler beim Aufruf der OpenAI API: 500: Fehler bei der Kommunikation mit OpenAI API",
|
|
"agent_type": "analyst_agent",
|
|
"agent_id": "analyst_agent",
|
|
"agent_name": "Data Analyst",
|
|
"result_format": "AnalysisReport"
|
|
},
|
|
{
|
|
"id": "msg_f95c1ada-d806-40a1-a07f-518d81d2780a",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:15:21.471738",
|
|
"finished_at": null,
|
|
"sequence_no": 3,
|
|
"status": "pending",
|
|
"role": "assistant",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [
|
|
{
|
|
"id": "doc_050476e5-fb45-4eac-aa22-5d34c08ce7e6",
|
|
"source": {
|
|
"type": "generated",
|
|
"id": "doc_050476e5-fb45-4eac-aa22-5d34c08ce7e6",
|
|
"name": "Frontend Architecture and Interaction Guide: Structure, Functionality, and Diagrams",
|
|
"content_type": "text/markdown",
|
|
"size": 6174
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "# Frontend Architecture and Interaction Guide: Structure, Functionality, and Diagrams\n\n## Introduction\n\n### Purpose\nThis document provides a comprehensive guide to the frontend architecture and interaction mechanisms of our workflow system. It aims to elucidate the structure, functionality, and interactions within the frontend components, offering insights into how they work together to deliver a seamless user experience.\n\n### Scope\nThe guide covers the following key areas:\n- The overall architecture and its components\n- Detailed functionality of each module\n- Interaction flow between modules\n- Visual aids and diagrams to enhance understanding\n\n## Architecture Overview\n\nThe frontend architecture is modular, comprising several interconnected components that manage different aspects of the workflow system. The primary modules include:\n\n- **Workflow UI Module**: Handles user interface components and rendering.\n- **Workflow Data Module**: Manages data operations and API communications.\n- **Workflow Features Module**: Implements core features like chat functionality and file handling.\n- **Workflow Coordination Module**: Coordinates interactions between UI and features, resolving dependencies.\n- **Utility Modules**: Provide helper functions and utilities.\n\n### Diagram: Frontend Architecture\n\n```plaintext\n+-------------------+ +-------------------+ +-------------------+\n| Workflow UI |<--->| Workflow Data |<--->| Workflow Features|\n+-------------------+ +-------------------+ +-------------------+\n ^ ^ ^\n | | |\n v v v\n+-------------------+ +-------------------+ +-------------------+\n| Workflow Utils |<--->| Workflow Coord. |<--->| Other Modules |\n+-------------------+ +-------------------+ +-------------------+\n```\n\n## Detailed Module Descriptions\n\n### Workflow UI Module\n\n#### Purpose\nThe UI module is responsible for rendering the user interface components, managing DOM elements, and handling user interactions.\n\n#### Key Components\n- **DOM Elements Initialization**: Sets up references to all necessary DOM elements.\n- **Event Listeners**: Manages user interactions such as button clicks and file uploads.\n- **Rendering Functions**: Updates the UI based on the current state, including logs, chat messages, and file previews.\n\n#### Example\n```javascript\nfunction initUI(workflowStateObj, callbackFunctions) {\n console.log(\"Initialisiere Workflow-UI...\");\n // Initialize DOM elements and event listeners\n initDomElements();\n setupEventListeners();\n // Render initial state\n renderLogs([]);\n renderChatMessages([], null);\n updateDataStatistics(0, 0);\n}\n```\n\n### Workflow Data Module\n\n#### Purpose\nThis module handles data management and API communications, ensuring data integrity and synchronization with the backend.\n\n#### Key Functions\n- **Data Initialization**: Sets up the data layer with global and workflow-specific states.\n- **API Communication**: Manages API calls for uploading files, fetching workflow status, and more.\n- **Data Statistics**: Tracks data sent and received for performance monitoring.\n\n#### Example\n```javascript\nasync function uploadAndAddFile(file, isInitial = false) {\n try {\n // Check if file already exists\n let existingFile = window.globalState.mainView.availableFiles.find(\n f => f.name === file.name && f.size === file.size\n );\n // Upload new file if not existing\n if (!existingFile) {\n const formData = new FormData();\n formData.append('file', file);\n processedFile = await window.globalUtils.uploadFile('/api/files/upload', formData);\n }\n } catch (error) {\n console.error(\"Fehler beim Hochladen/Hinzufügen der Datei:\", error);\n }\n}\n```\n\n### Workflow Features Module\n\n#### Purpose\nImplements core features such as chat functionality, file handling, and log management.\n\n#### Key Features\n- **Chat Functionality**: Manages chat interactions and message rendering.\n- **File Handling**: Supports file uploads, previews, and downloads.\n- **Log Management**: Maintains a log of workflow activities for debugging and tracking.\n\n#### Example\n```javascript\nfunction initFeatures(workflowStateObj, callbackFunctions) {\n console.log(\"Initialisiere Workflow-Features...\");\n // Initialize DOM elements and event listeners\n initializeDomElements();\n setupEventListeners();\n // Start with initial prompt view\n WorkflowCoordination.showInitialPromptView();\n}\n```\n\n### Workflow Coordination Module\n\n#### Purpose\nCoordinates interactions between the UI and feature modules, resolving circular dependencies and managing shared states.\n\n#### Key Responsibilities\n- **State Management**: Maintains the shared workflow state across modules.\n- **Interaction Coordination**: Manages the flow of data and events between modules.\n- **UI Updates**: Ensures consistent UI updates based on workflow state changes.\n\n#### Example\n```javascript\nfunction initCoordination(workflowStateObj, modules) {\n console.log(\"Initialisiere Workflow-Koordination...\");\n workflowState = workflowStateObj;\n WorkflowUI = modules.ui;\n WorkflowData = modules.data;\n}\n```\n\n## Conclusion\n\n### Key Takeaways\n- The frontend architecture is modular, promoting separation of concerns and ease of maintenance.\n- Each module has a specific role, contributing to the overall functionality of the workflow system.\n- Interaction between modules is well-coordinated, ensuring a seamless user experience.\n- The use of utility functions and shared states enhances the robustness and scalability of the system.\n\n### Future Enhancements\n- Further optimization of API calls to reduce latency.\n- Enhanced error handling and user feedback mechanisms.\n- Continuous integration of user feedback to improve UI/UX.\n\nThis guide serves as a foundational document for understanding the frontend architecture and interactions within our workflow system. It provides a basis for further development and optimization efforts.",
|
|
"is_extracted": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"content": "I've created a document titled 'Frontend Architecture and Interaction Guide: Structure, Functionality, and Diagrams' that contains the requested information. The document is attached to this message.",
|
|
"agent_type": "documentation_agent",
|
|
"agent_id": "documentation_agent",
|
|
"agent_name": "Documentation Specialist",
|
|
"result_format": "FormattedDocument"
|
|
},
|
|
{
|
|
"id": "msg_0dc911b2-0250-49c8-9df4-830bdfa72832",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:15:43.790349",
|
|
"finished_at": null,
|
|
"sequence_no": 4,
|
|
"status": "pending",
|
|
"role": "assistant",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [],
|
|
"content": "# Analysis Report: Evaluation of Frontend Features and Functionalities\n\n## 1. Summary of Main Themes and Topics\n\nThe document content and related activities focus on evaluating the frontend's features and functionalities, particularly in terms of user experience, efficiency, and integration potential with other systems. The main themes include:\n\n- **User Experience (UX):** How intuitive and user-friendly the frontend is for end-users.\n- **Efficiency:** The performance and responsiveness of the frontend in executing tasks.\n- **Integration Potential:** The ability of the frontend to integrate seamlessly with other systems and workflows.\n\n## 2. Key Insights and Observations\n\n### User Experience\n- The frontend is designed with a focus on usability, featuring intuitive navigation and clear visual cues.\n- The presence of drag-and-drop functionality for file uploads enhances user interaction by simplifying the process of adding files.\n- The use of collapsible sections and toggle buttons in the UI allows users to manage information density effectively, improving readability and focus.\n\n### Efficiency\n- The frontend supports real-time updates and interactions, as evidenced by the implementation of polling mechanisms for workflow status updates.\n- The system is designed to handle asynchronous operations, allowing users to continue interacting with the frontend while background processes are running.\n\n### Integration Potential\n- The frontend architecture supports modular integration, as seen in the use of generic entity modules for handling different data types (e.g., users, files, mandates).\n- The presence of API endpoints and structured data handling suggests that the frontend can be easily integrated with backend systems and third-party services.\n\n## 3. Analysis of Structure, Patterns, and Relationships\n\n### Structure\n- The frontend is structured around a modular design, with distinct components for user input, file management, and workflow coordination.\n- The use of a three-column layout in the UI provides a balanced distribution of information, enhancing user focus and accessibility.\n\n### Patterns\n- Consistent use of icons and badges across different modules (e.g., user roles, file types) helps in quick identification and categorization.\n- The implementation of event listeners and callbacks indicates a responsive design pattern, ensuring that user actions are promptly handled.\n\n### Relationships\n- The integration between frontend components and backend services is facilitated through well-defined API endpoints, ensuring data consistency and reliability.\n- The coordination between different modules (e.g., workflow features, UI components) is managed through shared state objects, promoting a cohesive user experience.\n\n## 4. Clear Conclusions and Interpretations\n\n- The frontend is well-equipped to provide a positive user experience through its intuitive design and efficient handling of tasks.\n- Its modular architecture and use of APIs make it highly adaptable for integration with other systems, enhancing its value in diverse operational environments.\n- The focus on real-time interactions and asynchronous processing ensures that the frontend can handle complex workflows without compromising on performance.\n\n## 5. Recommendations or Implications\n\n- **Enhance Accessibility:** Consider implementing additional accessibility features, such as keyboard shortcuts and screen reader support, to cater to a wider range of users.\n- **Expand Integration Capabilities:** Explore opportunities to integrate with more third-party services, leveraging the existing API infrastructure to enhance functionality.\n- **Continuous User Feedback:** Implement mechanisms for collecting user feedback directly through the frontend to identify areas for improvement and ensure ongoing user satisfaction.\n\nOverall, the frontend demonstrates a strong foundation in user experience, efficiency, and integration potential, making it a valuable asset for users and organizations alike.",
|
|
"agent_type": "analyst_agent",
|
|
"agent_id": "analyst_agent",
|
|
"agent_name": "Data Analyst",
|
|
"result_format": "AnalysisReport"
|
|
},
|
|
{
|
|
"id": "msg_7b2232f2-a415-4c81-95df-beaed2318425",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:16:16.386067",
|
|
"finished_at": null,
|
|
"sequence_no": 5,
|
|
"status": "pending",
|
|
"role": "assistant",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [
|
|
{
|
|
"id": "doc_7c2252b1-ad4e-49ea-a361-e3ce6d95b9d5",
|
|
"source": {
|
|
"type": "generated",
|
|
"id": "doc_7c2252b1-ad4e-49ea-a361-e3ce6d95b9d5",
|
|
"name": "Comprehensive Frontend Documentation: Structure, Functionality, and Value Proposition Analysis",
|
|
"content_type": "text/markdown",
|
|
"size": 3926
|
|
},
|
|
"contents": [
|
|
{
|
|
"type": "text",
|
|
"text": "# Comprehensive Frontend Documentation: Structure, Functionality, and Value Proposition Analysis\n\n## Introduction\n\nThis document provides a comprehensive overview of the frontend architecture, its functionality, and a value proposition analysis. The purpose is to offer a detailed understanding of how the frontend is structured, how it operates, and the benefits it provides to users and organizations. This documentation is intended for developers, project managers, and stakeholders who are involved in the development and maintenance of the frontend system.\n\n## Table of Contents\n\n1. [Frontend Structure](#frontend-structure)\n2. [Frontend Functionality](#frontend-functionality)\n3. [Value Proposition Analysis](#value-proposition-analysis)\n4. [Conclusion](#conclusion)\n\n## Frontend Structure\n\n### Overview\n\nThe frontend is designed with a modular architecture, allowing for flexibility and scalability. It is structured around distinct components that handle user input, file management, and workflow coordination.\n\n### Key Components\n\n- **User Input Module**: Manages user interactions and input data.\n- **File Management Module**: Handles file uploads, previews, and downloads.\n- **Workflow Coordination Module**: Manages the workflow processes and interactions between different components.\n\n### Layout\n\nThe frontend utilizes a three-column layout to distribute information effectively, enhancing user focus and accessibility. This layout supports a responsive design, ensuring compatibility across various devices.\n\n### Code Structure\n\nThe code is organized into modules, each responsible for specific functionalities. This modular approach facilitates easier maintenance and updates.\n\n## Frontend Functionality\n\n### User Experience (UX)\n\n- **Intuitive Navigation**: The frontend is designed with user-friendly navigation, featuring clear visual cues and logical flow.\n- **Interactive Elements**: Includes drag-and-drop functionality for file uploads and collapsible sections to manage information density.\n\n### Efficiency\n\n- **Real-Time Updates**: Supports real-time interactions through polling mechanisms for workflow status updates.\n- **Asynchronous Operations**: Allows users to continue interacting with the frontend while background processes are running.\n\n### Integration Potential\n\n- **Modular Integration**: Supports integration with backend systems and third-party services through well-defined API endpoints.\n- **Structured Data Handling**: Ensures data consistency and reliability across different modules.\n\n## Value Proposition Analysis\n\n### Key Insights\n\n- **User Experience**: The frontend provides a positive user experience through its intuitive design and efficient task handling.\n- **Adaptability**: Its modular architecture and API usage make it highly adaptable for integration with other systems.\n- **Performance**: Focus on real-time interactions and asynchronous processing ensures the frontend can handle complex workflows without compromising performance.\n\n### Recommendations\n\n- **Enhance Accessibility**: Implement additional accessibility features, such as keyboard shortcuts and screen reader support.\n- **Expand Integration Capabilities**: Explore opportunities to integrate with more third-party services.\n- **Continuous User Feedback**: Implement mechanisms for collecting user feedback to identify areas for improvement.\n\n## Conclusion\n\nThe frontend demonstrates a strong foundation in user experience, efficiency, and integration potential. Its modular design and real-time capabilities make it a valuable asset for users and organizations. By enhancing accessibility and expanding integration capabilities, the frontend can further increase its value proposition.\n\n---\n\nThis document provides a detailed analysis of the frontend's structure, functionality, and value proposition. It serves as a guide for understanding the system's capabilities and potential areas for improvement.",
|
|
"is_extracted": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"content": "I've created a document titled 'Comprehensive Frontend Documentation: Structure, Functionality, and Value Proposition Analysis' that contains the requested information. The document is attached to this message.",
|
|
"agent_type": "documentation_agent",
|
|
"agent_id": "documentation_agent",
|
|
"agent_name": "Documentation Specialist",
|
|
"result_format": "FormattedDocument"
|
|
},
|
|
{
|
|
"id": "msg_3e9394b8-b962-4452-87b0-a2c92f684c65",
|
|
"workflow_id": "dd16f4f5-9da7-4bc1-889e-242dcaba6ea8",
|
|
"parent_message_id": null,
|
|
"started_at": "2025-04-15T17:16:29.267887",
|
|
"finished_at": null,
|
|
"sequence_no": 6,
|
|
"status": "pending",
|
|
"role": "assistant",
|
|
"data_stats": {
|
|
"processing_time": 0.0,
|
|
"token_count": 0,
|
|
"bytes_sent": 0,
|
|
"bytes_received": 0
|
|
},
|
|
"documents": [],
|
|
"content": "**Summary of Workflow Results**\n\n1. **Key Findings and Results:**\n - **Initial Code Analysis:** The analyst_agent attempted to analyze the JavaScript files to understand the frontend's structure and functionality. However, the statistical analysis was not completed due to a communication error with the OpenAI API.\n - **Frontend Functionality Documentation:** The documentation_agent successfully created a detailed document titled 'Frontend Architecture and Interaction Guide: Structure, Functionality, and Diagrams,' which outlines the frontend's structure and functionality.\n - **Value Proposition Analysis:** The analyst_agent conducted an analysis focusing on the frontend's user experience, efficiency, and integration potential. The analysis highlighted the importance of intuitive design, performance, and system compatibility.\n - **Final Documentation and Summary:** The documentation_agent compiled all findings into a comprehensive document titled 'Comprehensive Frontend Documentation: Structure, Functionality, and Value Proposition Analysis,' which includes both the frontend description and its value proposition.\n\n2. **Connection to Original Task:**\n - The workflow effectively addressed the original task of analyzing and documenting the frontend's structure, functionality, and potential value. Despite the initial setback in statistical analysis, the subsequent activities provided a thorough understanding and documentation of the frontend.\n\n3. **Conclusions and Recommendations:**\n - The comprehensive documentation created serves as a valuable resource for understanding the frontend's architecture and its benefits. It is recommended to resolve the API communication issue for future analyses to ensure all aspects of the frontend are thoroughly evaluated. Additionally, leveraging the insights from the value proposition analysis can guide enhancements in user experience and system integration.",
|
|
"agent_type": "summary",
|
|
"agent_id": "workflow_summary",
|
|
"agent_name": "Workflow Summary",
|
|
"result_format": "Text",
|
|
"workflow_complete": true
|
|
}
|
|
],
|
|
"logs": [
|
|
{
|
|
"id": "log_67cb74c6-e3df-4701-a897-9d9b32e05583",
|
|
"message": "Starting workflow execution",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.752954",
|
|
"agent_id": "workflow",
|
|
"agent_name": "Workflow Management"
|
|
},
|
|
{
|
|
"id": "log_12de7dd1-4837-417b-be86-bfc98c477a53",
|
|
"message": "Processing 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.772966",
|
|
"agent_id": "files",
|
|
"agent_name": "File Processing"
|
|
},
|
|
{
|
|
"id": "log_26412f30-c19b-471b-bf26-fe0fb05a704f",
|
|
"message": "File prompts.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.813226",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_bdca1e05-3ead-4f32-a45e-dc13cbb5c9fe",
|
|
"message": "File users.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.848441",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_c1f3761c-0c51-4b2d-8bb4-3629b482ce24",
|
|
"message": "File files.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.883026",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_22ad03d8-5cc5-4ddf-99ab-5efe4a041fb4",
|
|
"message": "File workflow.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.909636",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_1e8966cc-79f3-4098-ac42-dc46f65eb22f",
|
|
"message": "File workflow_data.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.948403",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_d07cba49-6496-4d8a-91be-452a07d13201",
|
|
"message": "File workflow_utils.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:29.986346",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_abb71a64-df38-4f63-b6ff-e76d75da8282",
|
|
"message": "File workflow_coordination.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.024841",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_968e696b-3b7f-436d-91f1-3d054c82d0fe",
|
|
"message": "File mandates.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.068429",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b720a5a0-5373-4b7e-9e2a-a54c4a0e9da9",
|
|
"message": "File workflow_ui.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.110061",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_4fae185e-4bdc-4c1f-b9fb-769554f3e5cc",
|
|
"message": "File workflow_features.js read successfully (extracted: True)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.147139",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_763727cd-89c0-498d-9d6c-4dc883b137b9",
|
|
"message": "User message processed",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.465046",
|
|
"agent_id": "workflow",
|
|
"agent_name": "Workflow Management"
|
|
},
|
|
{
|
|
"id": "log_e9d53694-3bd3-49e1-91b3-99cc9d4ce308",
|
|
"message": "Creating agent-aware work plan",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:30.486175",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_234fc328-c680-4247-9b6e-bc6e5a9cba99",
|
|
"message": "Work plan created with 4 activities",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.555257",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_8e5ca9b7-00b3-45b3-8192-07e3e9f28073",
|
|
"message": "Activity 1: Initial Code Analysis (Agents: analyst_agent)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.577979",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_87c3583d-522e-4a78-9145-619401e572c1",
|
|
"message": " Document requirements: Examine all JavaScript files to extract information about the frontend structure, key components, and their interactions.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.598005",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_9111b7e9-b360-4d68-8e52-9c498e85cd89",
|
|
"message": "Activity 2: Frontend Functionality Documentation (Agents: documentation_agent)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.617979",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_294e8e9f-82f2-42c9-9316-0a9154b0a78b",
|
|
"message": " Document requirements: Use the AnalysisReport from the Initial Code Analysis to guide the documentation process.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.637407",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_e7b91ee9-2049-44be-be25-7e3b3a77e4c9",
|
|
"message": " Dependencies: Activities 1",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.658413",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_b96d13b3-365b-499f-b759-a57f8c2c954a",
|
|
"message": "Activity 3: Value Proposition Analysis (Agents: analyst_agent)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.678021",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_dcae54fb-6910-471a-92ba-337e908867e2",
|
|
"message": " Document requirements: Use insights from the AnalysisReport and FormattedDocument to assess the frontend's value proposition.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.697055",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_3f790068-53c6-474a-88dc-1c859c2c9db5",
|
|
"message": " Dependencies: Activities 1, 2",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.717838",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_e729c885-ed07-4392-bfc5-45e9903c1221",
|
|
"message": "Activity 4: Final Documentation and Summary (Agents: documentation_agent)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.738422",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_66514581-0ee6-4daa-9b23-a30ba025e493",
|
|
"message": " Document requirements: Integrate the FormattedDocument from the Frontend Functionality Documentation and the AnalysisReport from the Value Proposition Analysis.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.759462",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_2a589647-a05e-4457-8e66-f89493514212",
|
|
"message": " Dependencies: Activities 2, 3",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.780485",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_39cff029-a3d5-4fab-8d16-1383a28504c7",
|
|
"message": "Created agent-aware work plan with 4 activities",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.810482",
|
|
"agent_id": "planning",
|
|
"agent_name": "Activity Planning"
|
|
},
|
|
{
|
|
"id": "log_33d88dc4-ffaa-4277-a81d-12e471e21511",
|
|
"message": "Starting activity: Initial Code Analysis",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.832019",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_ff3bbe7f-965b-442d-b0f0-2c30715bca8a",
|
|
"message": "Document requirements: Examine all JavaScript files to extract information about the frontend structure, key components, and their interactions.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.859037",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_2db6be79-937b-47c1-a9ff-899b414f8d31",
|
|
"message": "Found 10 files in workflow",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.878878",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_c279e27b-751d-4746-a21e-bb68ca6f05cf",
|
|
"message": "Starting document extraction process",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.898462",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_1da4deb8-58be-4448-bc5f-5bccae84f0b2",
|
|
"message": "Starting data extraction with 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.918908",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_8bf48fc9-eb54-4ae1-b896-b0a149974252",
|
|
"message": "Creating extraction plan",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.938491",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_5f7efd07-9fc9-4fe4-8efd-77ff4431011b",
|
|
"message": "Extraktionsplan wird erstellt...",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:42.958568",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_c7c6c623-dca8-4628-93d4-6cc9240e457e",
|
|
"message": "Extraktionsplan erstellt für 10 Dateien",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.353395",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_763f2a40-d0ed-4425-8fec-4dfdb90393aa",
|
|
"message": "Extraction plan created: 10 files, 0 need extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.374402",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3160edbe-1e46-4327-a29c-69cee2244e6e",
|
|
"message": "Using document handler for extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.396469",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_416c1abd-51b9-4928-a878-2eca4429d73e",
|
|
"message": "Processing file: workflow_ui.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.420470",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_d27e76a1-bcde-4da5-8c89-729f50249852",
|
|
"message": "Using existing content for workflow_ui.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.440981",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_86747357-bba9-4ee8-a5e4-ab1978e2ceb1",
|
|
"message": "Processing file: workflow_features.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.461021",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_c5ecedd1-a28a-43f5-9362-5689f8609a88",
|
|
"message": "Using existing content for workflow_features.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.482147",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_63001216-26d8-4f39-9d44-c5f5e6384dc5",
|
|
"message": "Processing file: workflow.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.503145",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_c179038d-e6d5-431a-be54-88f024417ef0",
|
|
"message": "Using existing content for workflow.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.523164",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_e95c40ff-2653-4bff-af5e-57e5d182f31a",
|
|
"message": "Processing file: workflow_data.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.544155",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b0df704c-2491-4f7d-83bc-3b3088217834",
|
|
"message": "Using existing content for workflow_data.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.565159",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_ed3a92eb-6775-4e51-9b61-a02726add438",
|
|
"message": "Processing file: workflow_utils.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.585361",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_6281c85e-737c-410a-984e-235e61e1b6d2",
|
|
"message": "Using existing content for workflow_utils.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.605338",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_6ccadb48-24cd-4ae4-a2f0-ba8c0457b574",
|
|
"message": "Processing file: workflow_coordination.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.626334",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_93bdee1b-1dce-4f45-8b4d-9e108eb37188",
|
|
"message": "Using existing content for workflow_coordination.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.647522",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b663b1fc-198a-41c6-ba8d-1a0882f1148f",
|
|
"message": "Processing file: prompts.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.668417",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_36cbbcf1-ece4-41bf-98ad-0ea72f8cf097",
|
|
"message": "Using existing content for prompts.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.688492",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_4375f46e-800f-449a-98d4-697a92b3fe8f",
|
|
"message": "Processing file: users.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.709068",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3e605623-6260-46ff-8709-a59179117318",
|
|
"message": "Using existing content for users.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.730668",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_679a18dc-dd47-4273-95e0-059226836b24",
|
|
"message": "Processing file: files.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.751047",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3a410ab8-a579-4270-a56c-f1f9695a3b13",
|
|
"message": "Using existing content for files.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.771061",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_66aac766-cd27-4fce-bbb6-27d493b38b0a",
|
|
"message": "Processing file: mandates.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.792386",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_284ff441-d32c-491f-afc2-4b00eb69b2b5",
|
|
"message": "Using existing content for mandates.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.812396",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_879bfed3-7e29-4f5e-a54a-7d450c9f993b",
|
|
"message": "Structuring extracted data from 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.832984",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b474e4d8-2fee-4a58-9868-67695d9f3e3f",
|
|
"message": "Creating contextual summaries for extracted content",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:13:47.854186",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_edf1e420-9a51-455e-a4ef-e33082dff17b",
|
|
"message": "Added contextual summaries to extracted data",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.081308",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_d661f8ee-1ee9-4313-aa3e-ca84a6b17b28",
|
|
"message": "Extracted content from 10 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.102310",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_9d171b2e-ae65-4e75-a82d-2d9588853d0b",
|
|
"message": "Document: workflow_ui.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.123896",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_d01286b0-07ba-4bd6-99ec-da81d838f1c9",
|
|
"message": "Document: workflow_features.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.144910",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_3ad329aa-9272-44a9-b372-d12756f1202f",
|
|
"message": "Document: workflow.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.177040",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_187304de-3f42-446b-b340-4d9f739134f1",
|
|
"message": "Document: workflow_data.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.212556",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_da4b5e64-11ab-4064-90f0-c9fe2b59575d",
|
|
"message": "Document: workflow_utils.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.238072",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_35d84061-c398-4827-a0b9-5dfc4882a59c",
|
|
"message": "Document: workflow_coordination.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.259504",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_3c7aedd4-e963-4aeb-84f1-e861cc364a1e",
|
|
"message": "Document: prompts.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.280036",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_9ae6d932-3e40-4351-aeb6-e848e107c36b",
|
|
"message": "Document: users.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.301040",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_a9a3d70c-7735-4620-b322-49b1a574faa5",
|
|
"message": "Document: files.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.322062",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_438a9cdd-b9ff-4686-9dbb-2b54096525e5",
|
|
"message": "Document: mandates.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.343629",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_a3e6ac5e-fbbd-4225-9857-b67c2e3d5dbf",
|
|
"message": "Passing 10 documents from user message to analyst_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.364214",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_23d70936-fb1a-4a5e-acd7-bb5b24e807c5",
|
|
"message": "Executing agent: analyst_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:02.384774",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_96060677-c163-4319-958c-26ab1341b2b9",
|
|
"message": "Agent analyst_agent produced 0 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:03.761169",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_d0cb4295-add1-47e2-9560-dbea06cfb353",
|
|
"message": "Completed activity: Initial Code Analysis",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:03.782758",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_52b68cef-c04b-41bd-9aae-5067ef485cb1",
|
|
"message": "Starting activity: Frontend Functionality Documentation",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.456206",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_df71c6c9-14d9-48fc-8c6b-c7ecbeb72534",
|
|
"message": "Document requirements: Use the AnalysisReport from the Initial Code Analysis to guide the documentation process.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.476790",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_76cbf1ac-b374-4390-af66-2b71ff4c8ac8",
|
|
"message": "Found 10 files in workflow",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.499956",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_858d1973-7f66-4303-9539-ad6b5066e248",
|
|
"message": "Starting document extraction process",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.521134",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_9d32127a-1f55-4628-9eb4-dfe8e2b11149",
|
|
"message": "Starting data extraction with 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.552693",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_6e3b83da-82d9-489d-9a9e-07d6a40f92a4",
|
|
"message": "Creating extraction plan",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.573876",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b1a4f892-c379-4be8-9b0f-3ed536fad13f",
|
|
"message": "Extraktionsplan wird erstellt...",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:05.594896",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_f6f5c50c-b160-4216-b41f-0536d4226554",
|
|
"message": "Extraktionsplan erstellt für 10 Dateien",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:09.990828",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_f477d08a-6dd8-4501-bcea-dc3aac29ac45",
|
|
"message": "Extraction plan created: 10 files, 0 need extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.013339",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_07deefcf-c415-44bd-a32f-ca8499805b36",
|
|
"message": "Using document handler for extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.034860",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_e630d54f-5558-4999-a468-0e351050f878",
|
|
"message": "Processing file: workflow_data.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.058856",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_611cba48-9e7d-4daf-8c16-a40a5b6896d6",
|
|
"message": "Using existing content for workflow_data.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.080445",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_b2092996-5939-4bdc-af2b-3024b67ec442",
|
|
"message": "Processing file: workflow_ui.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.104021",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_75b14a5e-6c3b-46b5-b480-31a57140ff17",
|
|
"message": "Using existing content for workflow_ui.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.124595",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_61080258-4609-46eb-8782-4424acb5743f",
|
|
"message": "Processing file: workflow_features.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.145637",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_275406ad-ee80-4e77-8ee8-8b54b2179c43",
|
|
"message": "Using existing content for workflow_features.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.167600",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_62fa5904-b53f-4c2d-839d-79a3e253fd0f",
|
|
"message": "Processing file: workflow.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.190232",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_40711a18-c9c6-4e91-b9f0-1cf8fc36fc7f",
|
|
"message": "Using existing content for workflow.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.211820",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_7d90de01-bfbf-448d-a0e6-0b313a26e3c1",
|
|
"message": "Processing file: workflow_utils.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.232823",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_cbb27d83-14cb-4c0a-88ff-8b7788d88788",
|
|
"message": "Using existing content for workflow_utils.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.254335",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_e7de3b5f-0603-45ea-941d-11a9c8d870fb",
|
|
"message": "Processing file: workflow_coordination.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.275679",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_6db464c1-ef3b-41ec-ae5d-613f729df31a",
|
|
"message": "Using existing content for workflow_coordination.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.296296",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_cd620537-12ec-4d8b-9310-68355a6619f9",
|
|
"message": "Processing file: prompts.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.317815",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3e479f53-9570-49fd-b173-6bd99fe2fc0b",
|
|
"message": "Using existing content for prompts.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.339407",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_cdc3da54-9ce2-46bc-9f1b-936b9d1eee05",
|
|
"message": "Processing file: users.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.362432",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_5a258f2f-4e70-4880-a82d-7b67296fe5d2",
|
|
"message": "Using existing content for users.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.383010",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_9d5a04a4-35aa-4336-9d32-5b05d929f9fa",
|
|
"message": "Processing file: files.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.404608",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_6315cb77-fc36-4c75-89c3-9503e6f3a3d6",
|
|
"message": "Using existing content for files.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.425612",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_7537cd9d-1858-4e3f-8e25-29913139da76",
|
|
"message": "Processing file: mandates.js (Extraction needed: False)",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.446043",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3855ed06-5087-40d8-a60b-ae4ba37f2f99",
|
|
"message": "Using existing content for mandates.js",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.467071",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_1536f22f-6e84-497c-b7ed-c2ad89c88877",
|
|
"message": "Structuring extracted data from 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.487678",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_8773b140-2bed-4b57-9a6f-bb1f2ca34530",
|
|
"message": "Creating contextual summaries for extracted content",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:10.509115",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_7ebb7ff6-6d31-4290-bac7-6b3324b96790",
|
|
"message": "Added contextual summaries to extracted data",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.858519",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_4690ceea-e55d-4bbe-aae6-9d7869b72637",
|
|
"message": "Extracted content from 10 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.887227",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_f17f5501-3089-4322-bbdd-f74bf37b52b9",
|
|
"message": "Document: workflow_data.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.914370",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_c5214ca2-4199-4f1e-9bda-44acc7b94ecf",
|
|
"message": "Document: workflow_ui.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.935715",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_fd9f6a60-117e-487a-a6a0-3b935dbc25e6",
|
|
"message": "Document: workflow_features.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.958718",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_a8c40ba3-f3c7-4234-a069-b0816f3669f6",
|
|
"message": "Document: workflow.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:21.980247",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_bde3a572-281d-46ce-bcae-500fe9b2e623",
|
|
"message": "Document: workflow_utils.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.001278",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_82d0765a-25ce-4025-8d98-6f5b385185b9",
|
|
"message": "Document: workflow_coordination.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.023572",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_5a3b49c1-23e4-4a7b-abd8-d5f735651f89",
|
|
"message": "Document: prompts.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.046002",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_8bf5db6f-aaf3-4a2f-b8cb-c759bce0cfb0",
|
|
"message": "Document: users.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.067078",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_e124984c-ec25-426e-95f1-792bf9fd0f9f",
|
|
"message": "Document: files.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.089060",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_43bbea22-f27c-49c3-aa6b-ef86266f6fad",
|
|
"message": "Document: mandates.js, Method: existing_content, Extracted: True",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.111060",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_b0c5c8fa-6ef4-48f2-bdac-37fb92901aee",
|
|
"message": "Passing 10 documents from user message to documentation_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.132101",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_d983701b-afa4-4966-98ce-bcc6a5d01c25",
|
|
"message": "Executing agent: documentation_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:14:22.156005",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_63f2e4c9-e0c6-4f10-a044-88b16ecdc9ac",
|
|
"message": "Agent documentation_agent produced 1 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:21.471738",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_7037d793-f2cc-4a30-b852-a02e368b2d39",
|
|
"message": "Completed activity: Frontend Functionality Documentation",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:21.494364",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_eb78cc40-b6e5-4e74-b3e1-9f98aa4953da",
|
|
"message": "Starting activity: Value Proposition Analysis",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.383181",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_b71f1091-66f3-44e1-a506-d78b53479edd",
|
|
"message": "Document requirements: Use insights from the AnalysisReport and FormattedDocument to assess the frontend's value proposition.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.404184",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_c4032fbf-ae9d-4200-8510-1c97a1c4db24",
|
|
"message": "Found 10 files in workflow",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.426248",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_966f844f-233a-4bb0-bb97-864465bffee1",
|
|
"message": "Starting document extraction process",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.448835",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_fcd43410-f7a6-4868-acf4-a2bcbe12e165",
|
|
"message": "Starting data extraction with 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.469868",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_06ecd8b2-2274-481c-96c7-7d008908eebb",
|
|
"message": "Creating extraction plan",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.494344",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_2bcbf3f4-284a-4494-8303-e3519c5e9786",
|
|
"message": "Extraktionsplan wird erstellt...",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:24.515880",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_470cb8e9-4b80-44ec-a62c-b1a9e115f1f0",
|
|
"message": "Extraktionsplan erstellt für 0 Dateien",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.239991",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_1afa2ac7-0ba7-4b46-94cf-babf66960301",
|
|
"message": "Using document handler for extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.263140",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_2a49933f-24c3-422b-aaeb-bde57ad5e9a7",
|
|
"message": "Structuring extracted data from 0 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.285082",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_3a0a059c-6d01-4a6e-861d-6a52c1a878cc",
|
|
"message": "Extracted content from 0 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.309102",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_c7b47b67-57de-450b-8483-7252a7efc862",
|
|
"message": "Passing 10 documents from user message to analyst_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.333205",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_8e870633-8561-4427-80b9-2726682de125",
|
|
"message": "Executing agent: analyst_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:25.356330",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_d6670de1-8fdd-43a1-b3d8-a6a1edc9b325",
|
|
"message": "Agent analyst_agent produced 0 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:43.791351",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_166e3863-b6d4-4b89-a752-2f40162c846e",
|
|
"message": "Completed activity: Value Proposition Analysis",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:43.814360",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_0cd1ad31-829f-4d3c-b3fc-4e488abd93f3",
|
|
"message": "Starting activity: Final Documentation and Summary",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.148419",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_1319c06c-a527-4518-9868-4a0af4c8bf74",
|
|
"message": "Document requirements: Integrate the FormattedDocument from the Frontend Functionality Documentation and the AnalysisReport from the Value Proposition Analysis.",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.175424",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_bf54ddfc-68c3-47c0-95ee-6f7ffc45a546",
|
|
"message": "Found 10 files in workflow",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.196973",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_6e4e10b6-46f1-4811-8f50-9d0572c06947",
|
|
"message": "Starting document extraction process",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.218416",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_e8a17248-ef6e-4834-a9ae-f6b13ae3a9e6",
|
|
"message": "Starting data extraction with 10 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.240826",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_87e4c2aa-9746-44d7-bb43-3ea3e4859c17",
|
|
"message": "Creating extraction plan",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.262857",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_051a7c7a-883d-42c8-88ad-892bd056410b",
|
|
"message": "Extraktionsplan wird erstellt...",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.288411",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_76d7f5d9-0f2b-4381-a753-80364f76fa42",
|
|
"message": "Extraktionsplan erstellt für 0 Dateien",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:47.986589",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_756dacca-eb32-4876-9443-b08feb338599",
|
|
"message": "Using document handler for extraction",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:48.008101",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_9122446b-cd5a-4079-97c6-4a62e24bd07f",
|
|
"message": "Structuring extracted data from 0 files",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:48.030708",
|
|
"agent_id": null,
|
|
"agent_name": null
|
|
},
|
|
{
|
|
"id": "log_1357eeaf-4c6e-4cfb-9897-ca1d7e0b7cb9",
|
|
"message": "Extracted content from 0 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:48.052276",
|
|
"agent_id": "extraction",
|
|
"agent_name": "extraction"
|
|
},
|
|
{
|
|
"id": "log_8ed6137f-2434-4dcd-962b-4aa061597f84",
|
|
"message": "Passing 10 documents from user message to documentation_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:48.074676",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_571bddec-395a-44d4-8b3e-7bc180c7a8dc",
|
|
"message": "Executing agent: documentation_agent",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:15:48.097189",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_c367799d-0cb0-496b-8e2e-2d23ba99aa7a",
|
|
"message": "Agent documentation_agent produced 1 documents",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:16:16.387066",
|
|
"agent_id": "agents",
|
|
"agent_name": "Agent Selection & Execution"
|
|
},
|
|
{
|
|
"id": "log_f9c65bda-eaaf-44e2-98be-1acf794408d9",
|
|
"message": "Completed activity: Final Documentation and Summary",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:16:16.409078",
|
|
"agent_id": "execution",
|
|
"agent_name": "Activity Execution"
|
|
},
|
|
{
|
|
"id": "log_181176fc-59fb-4d89-8c58-58b5186273c4",
|
|
"message": "Created workflow summary",
|
|
"type": "info",
|
|
"timestamp": "2025-04-15T17:16:29.267887",
|
|
"agent_id": "summary",
|
|
"agent_name": "Results Summary"
|
|
}
|
|
],
|
|
"data_stats": {
|
|
"total_processing_time": 183.380771,
|
|
"total_token_count": 0,
|
|
"total_bytes_sent": 0,
|
|
"total_bytes_received": 0
|
|
},
|
|
"completed_at": "2025-04-15T17:16:33.127725"
|
|
} |