separated gateway from frontend
This commit is contained in:
parent
fce4c721c6
commit
5a0c7e0ae8
30 changed files with 27 additions and 2504 deletions
|
|
@ -1,15 +0,0 @@
|
||||||
<!-- Meine Agents Ansicht -->
|
|
||||||
<div id="agents-view">
|
|
||||||
<h2>Meine Agents</h2>
|
|
||||||
<div class="card">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>Verfügbare Agents</h3>
|
|
||||||
<button class="add-btn" id="add-agent-btn">
|
|
||||||
<i class="fas fa-plus"></i> Neuen Agent hinzufügen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="agents-list-container">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
(function() {
|
|
||||||
// DOM-Elemente
|
|
||||||
let addAgentBtn, agentsListContainer;
|
|
||||||
|
|
||||||
// Initialisierungsfunktion für das Agenten-Modul
|
|
||||||
function initAgentsModule(globalState) {
|
|
||||||
// DOM-Elemente referenzieren
|
|
||||||
addAgentBtn = document.getElementById('add-agent-btn');
|
|
||||||
agentsListContainer = document.getElementById('agents-list-container');
|
|
||||||
|
|
||||||
// Event-Listener hinzufügen
|
|
||||||
setupEventListeners(globalState);
|
|
||||||
|
|
||||||
// Initialdaten rendern
|
|
||||||
renderMyAgents(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener einrichten
|
|
||||||
function setupEventListeners(globalState) {
|
|
||||||
// Neuen Agenten hinzufügen
|
|
||||||
addAgentBtn.addEventListener('click', () => {
|
|
||||||
openAgentCreationModal(globalState);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modal zum Erstellen eines neuen Agenten öffnen
|
|
||||||
function openAgentCreationModal(globalState) {
|
|
||||||
// Einfaches Prompt als Platzhalter - in einer realen Anwendung würde dies
|
|
||||||
// durch ein ansprechendes Modal oder Formular ersetzt
|
|
||||||
const agentName = prompt('Name des neuen Agenten:');
|
|
||||||
const agentType = prompt('Typ des Agenten (z.B. Analyse, Transformation):');
|
|
||||||
const agentDescription = prompt('Beschreibung des Agenten:');
|
|
||||||
|
|
||||||
if (agentName && agentType) {
|
|
||||||
const newAgent = {
|
|
||||||
id: `agent_${Date.now()}`,
|
|
||||||
name: agentName,
|
|
||||||
type: agentType,
|
|
||||||
description: agentDescription || '',
|
|
||||||
workspace_id: globalState.currentWorkspace?.id,
|
|
||||||
capabilities: []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Agent zur globalen Liste hinzufügen
|
|
||||||
globalState.availableAgents.push(newAgent);
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyAgents(globalState);
|
|
||||||
|
|
||||||
// Optional: Agent speichern
|
|
||||||
saveAgent(newAgent, globalState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meine Agenten rendern
|
|
||||||
function renderMyAgents(globalState) {
|
|
||||||
console.info()
|
|
||||||
if (!agentsListContainer) {
|
|
||||||
console.error("No agents container")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
agentsListContainer.innerHTML = '';
|
|
||||||
|
|
||||||
// Agenten für den aktuellen Workspace filtern
|
|
||||||
const workspaceAgents = globalState.availableAgents.filter(
|
|
||||||
agent => agent.workspace_id === globalState.currentWorkspace.id
|
|
||||||
);
|
|
||||||
// console.info("debug: agentid-wsid: "+ (globalState.availableAgents[0].workspace_id) + " - " + globalState.currentWorkspace.id + ", wsAgents: " + workspaceAgents.length)
|
|
||||||
|
|
||||||
if (workspaceAgents.length === 0) {
|
|
||||||
agentsListContainer.innerHTML = '<div class="empty-state">Keine Agenten vorhanden.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
workspaceAgents.forEach(agent => {
|
|
||||||
const agentItem = document.createElement('div');
|
|
||||||
agentItem.className = 'agent-list-item';
|
|
||||||
agentItem.innerHTML = `
|
|
||||||
<div class="agent-header">
|
|
||||||
<h4>${agent.name}</h4>
|
|
||||||
<span class="agent-type">${agent.type}</span>
|
|
||||||
</div>
|
|
||||||
<div class="agent-description">${agent.description || ''}</div>
|
|
||||||
<div class="agent-capabilities">
|
|
||||||
${agent.capabilities ? agent.capabilities.map(cap =>
|
|
||||||
`<span class="capability-tag">${cap}</span>`
|
|
||||||
).join('') : ''}
|
|
||||||
</div>
|
|
||||||
<div class="agent-actions">
|
|
||||||
<button class="edit-agent-btn" data-id="${agent.id}">
|
|
||||||
<i class="fas fa-edit"></i> Bearbeiten
|
|
||||||
</button>
|
|
||||||
<button class="delete-agent-btn" data-id="${agent.id}">
|
|
||||||
<i class="fas fa-trash"></i> Löschen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
agentsListContainer.appendChild(agentItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event-Listener für Agenten-Aktionen einrichten
|
|
||||||
setupAgentActionListeners(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener für Agenten-Aktionen
|
|
||||||
function setupAgentActionListeners(globalState) {
|
|
||||||
// Bearbeiten-Button
|
|
||||||
document.querySelectorAll('.edit-agent-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const agentId = btn.getAttribute('data-id');
|
|
||||||
const agent = globalState.availableAgents.find(a => a.id === agentId);
|
|
||||||
|
|
||||||
if (agent) {
|
|
||||||
editAgent(agent, globalState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Löschen-Button
|
|
||||||
document.querySelectorAll('.delete-agent-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const agentId = btn.getAttribute('data-id');
|
|
||||||
|
|
||||||
if (confirm('Möchten Sie diesen Agenten wirklich löschen?')) {
|
|
||||||
deleteAgent(agentId, globalState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent bearbeiten
|
|
||||||
function editAgent(agent, globalState) {
|
|
||||||
// Einfaches Prompt als Platzhalter - in einer realen Anwendung würde dies
|
|
||||||
// durch ein ansprechendes Modal oder Formular ersetzt
|
|
||||||
const newName = prompt('Name des Agenten:', agent.name);
|
|
||||||
const newType = prompt('Typ des Agenten:', agent.type);
|
|
||||||
const newDescription = prompt('Beschreibung des Agenten:', agent.description);
|
|
||||||
|
|
||||||
if (newName) {
|
|
||||||
// Agent aktualisieren
|
|
||||||
agent.name = newName;
|
|
||||||
agent.type = newType || agent.type;
|
|
||||||
agent.description = newDescription || agent.description;
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyAgents(globalState);
|
|
||||||
|
|
||||||
// Optional: Aktualisierung auf dem Server
|
|
||||||
updateAgent(agent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent speichern
|
|
||||||
async function saveAgent(agentData, globalState) {
|
|
||||||
try {
|
|
||||||
// Agent auf dem Server speichern
|
|
||||||
const response = await window.globalUtils.postData('/api/agents/save', agentData);
|
|
||||||
|
|
||||||
// Rückmeldung vom Server verarbeiten
|
|
||||||
if (response && response.id) {
|
|
||||||
// Wenn der Server eine ID zurückgibt, diese aktualisieren
|
|
||||||
const index = globalState.availableAgents.findIndex(a => a.id === agentData.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
globalState.availableAgents[index].id = response.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyAgents(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Speichern des Agenten:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent aktualisieren
|
|
||||||
async function updateAgent(agent) {
|
|
||||||
try {
|
|
||||||
// Agent auf dem Server aktualisieren
|
|
||||||
await window.globalUtils.postData('/api/agents/update', agent);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Aktualisieren des Agenten:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent löschen
|
|
||||||
async function deleteAgent(agentId, globalState) {
|
|
||||||
try {
|
|
||||||
// Aus dem globalen State entfernen
|
|
||||||
globalState.availableAgents = globalState.availableAgents.filter(a => a.id !== agentId);
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyAgents(globalState);
|
|
||||||
|
|
||||||
// Agent vom Server löschen
|
|
||||||
await window.globalUtils.postData('/api/agents/delete', { id: agentId });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Löschen des Agenten:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modul-Initialisierung exportieren
|
|
||||||
window.initAgentsModule = initAgentsModule;
|
|
||||||
})();
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<!-- Meine Daten Ansicht -->
|
|
||||||
<div id="data-view">
|
|
||||||
<h2>Meine Daten</h2>
|
|
||||||
<div class="card">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>Verfügbare Dateien</h3>
|
|
||||||
<button class="upload-btn" id="data-upload-btn">
|
|
||||||
<i class="fas fa-upload"></i> Neue Dateien hochladen
|
|
||||||
</button>
|
|
||||||
<input type="file" id="data-file-input" multiple hidden>
|
|
||||||
</div>
|
|
||||||
<div class="files-container">
|
|
||||||
<ul class="file-list" id="my-files-list">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
// data-module.js
|
|
||||||
(function() {
|
|
||||||
// DOM-Elemente
|
|
||||||
let dataFileInput, uploadBtn, myFilesList;
|
|
||||||
|
|
||||||
// Initialisierungsfunktion für das Daten-Modul
|
|
||||||
function initDataModule(globalState) {
|
|
||||||
// DOM-Elemente referenzieren
|
|
||||||
dataFileInput = document.getElementById('data-file-input');
|
|
||||||
uploadBtn = document.getElementById('data-upload-btn');
|
|
||||||
myFilesList = document.getElementById('my-files-list');
|
|
||||||
|
|
||||||
// Event-Listener hinzufügen
|
|
||||||
setupEventListeners(globalState);
|
|
||||||
|
|
||||||
// Initialdaten rendern
|
|
||||||
renderMyFiles(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener einrichten
|
|
||||||
function setupEventListeners(globalState) {
|
|
||||||
// Datei-Upload
|
|
||||||
uploadBtn.addEventListener('click', () => dataFileInput.click());
|
|
||||||
dataFileInput.addEventListener('change',
|
|
||||||
window.globalUtils.handleFileUpload(dataFileInput, (newFile) => {
|
|
||||||
// Nach erfolgreichem Upload
|
|
||||||
globalState.availableFiles.push(newFile);
|
|
||||||
renderMyFiles(globalState);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meine Dateien rendern
|
|
||||||
function renderMyFiles(globalState) {
|
|
||||||
if (!myFilesList) return;
|
|
||||||
|
|
||||||
myFilesList.innerHTML = '';
|
|
||||||
|
|
||||||
if (globalState.availableFiles.length === 0) {
|
|
||||||
myFilesList.innerHTML = '<div class="empty-state">Keine Dateien vorhanden.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalState.availableFiles.forEach(file => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = 'file-item';
|
|
||||||
li.innerHTML = `
|
|
||||||
<div class="file-item-name">
|
|
||||||
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
|
|
||||||
<span>${file.name}</span>
|
|
||||||
</div>
|
|
||||||
<div class="file-item-info">
|
|
||||||
<span>${file.size || ''}</span>
|
|
||||||
<span>${new Date(file.upload_date || Date.now()).toLocaleString()}</span>
|
|
||||||
</div>
|
|
||||||
<div class="file-actions">
|
|
||||||
<button class="view-file-btn" data-id="${file.id}">
|
|
||||||
<i class="fas fa-eye"></i>
|
|
||||||
</button>
|
|
||||||
<button class="delete-file-btn" data-id="${file.id}">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
myFilesList.appendChild(li);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event-Listener für Datei-Aktionen
|
|
||||||
setupFileActionListeners(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener für Datei-Aktionen
|
|
||||||
function setupFileActionListeners(globalState) {
|
|
||||||
// Datei anzeigen
|
|
||||||
document.querySelectorAll('.view-file-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const fileId = btn.getAttribute('data-id');
|
|
||||||
const file = globalState.availableFiles.find(f => f.id === fileId);
|
|
||||||
if (file && file.path) {
|
|
||||||
window.open(file.path, '_blank');
|
|
||||||
} else {
|
|
||||||
alert('Datei nicht verfügbar');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Datei löschen
|
|
||||||
document.querySelectorAll('.delete-file-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', async () => {
|
|
||||||
const fileId = btn.getAttribute('data-id');
|
|
||||||
if (confirm('Möchten Sie diese Datei wirklich löschen?')) {
|
|
||||||
try {
|
|
||||||
// TODO: API-Endpunkt zum Löschen implementieren
|
|
||||||
// await fetch(`/files/${fileId}`, { method: 'DELETE' });
|
|
||||||
|
|
||||||
// Aus dem globalen State entfernen
|
|
||||||
globalState.availableFiles = globalState.availableFiles.filter(f => f.id !== fileId);
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyFiles(globalState);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Löschen der Datei:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modul-Initialisierung exportieren
|
|
||||||
window.initDataModule = initDataModule;
|
|
||||||
})();
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!-- Meine Prompts Ansicht -->
|
|
||||||
<div id="prompts-view">
|
|
||||||
<h2>Meine Prompts</h2>
|
|
||||||
<div class="card">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>Gespeicherte Prompts</h3>
|
|
||||||
<button class="add-btn" id="add-prompt-btn">
|
|
||||||
<i class="fas fa-plus"></i> Neuen Prompt erstellen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="prompts-list">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
// prompts-module.js
|
|
||||||
(function() {
|
|
||||||
// DOM-Elemente
|
|
||||||
let addPromptBtn, promptsList;
|
|
||||||
|
|
||||||
// Initialisierungsfunktion für das Prompts-Modul
|
|
||||||
function initPromptsModule(globalState) {
|
|
||||||
// DOM-Elemente referenzieren
|
|
||||||
addPromptBtn = document.getElementById('add-prompt-btn');
|
|
||||||
promptsList = document.getElementById('prompts-list');
|
|
||||||
|
|
||||||
// Event-Listener hinzufügen
|
|
||||||
setupEventListeners(globalState);
|
|
||||||
|
|
||||||
// Initialdaten rendern
|
|
||||||
renderMyPrompts(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener einrichten
|
|
||||||
function setupEventListeners(globalState) {
|
|
||||||
// Neuen Prompt hinzufügen
|
|
||||||
addPromptBtn.addEventListener('click', () => {
|
|
||||||
const newPrompt = prompt('Geben Sie den neuen Prompt ein:');
|
|
||||||
if (newPrompt && newPrompt.trim()) {
|
|
||||||
const promptData = {
|
|
||||||
id: `prompt_${Date.now()}`,
|
|
||||||
content: newPrompt.trim(),
|
|
||||||
created_at: new Date().toISOString()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prompt zur globalen Liste hinzufügen
|
|
||||||
globalState.availablePrompts.push(promptData);
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyPrompts(globalState);
|
|
||||||
|
|
||||||
// Optional: Prompt speichern
|
|
||||||
savePrompt(promptData, globalState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meine Prompts rendern
|
|
||||||
function renderMyPrompts(globalState) {
|
|
||||||
if (!promptsList) return;
|
|
||||||
|
|
||||||
promptsList.innerHTML = '';
|
|
||||||
|
|
||||||
if (globalState.availablePrompts.length === 0) {
|
|
||||||
promptsList.innerHTML = '<div class="empty-state">Keine Prompts vorhanden.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalState.availablePrompts.forEach(prompt => {
|
|
||||||
const promptItem = document.createElement('div');
|
|
||||||
promptItem.className = 'prompt-item';
|
|
||||||
promptItem.innerHTML = `
|
|
||||||
<div class="prompt-content">${prompt.content}</div>
|
|
||||||
<div class="prompt-meta">Erstellt am: ${new Date(prompt.created_at).toLocaleString()}</div>
|
|
||||||
<div class="prompt-actions">
|
|
||||||
<button class="use-prompt-btn" data-id="${prompt.id}">
|
|
||||||
<i class="fas fa-play"></i> Verwenden
|
|
||||||
</button>
|
|
||||||
<button class="delete-prompt-btn" data-id="${prompt.id}">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
promptsList.appendChild(promptItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event-Listener für Prompt-Aktionen einrichten
|
|
||||||
setupPromptActionListeners(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener für Prompt-Aktionen
|
|
||||||
function setupPromptActionListeners(globalState) {
|
|
||||||
// "Verwenden"-Button
|
|
||||||
document.querySelectorAll('.use-prompt-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const promptId = btn.getAttribute('data-id');
|
|
||||||
const selectedPrompt = globalState.availablePrompts.find(p => p.id === promptId);
|
|
||||||
|
|
||||||
if (selectedPrompt) {
|
|
||||||
// Workflow-Modul aktivieren und Prompt übernehmen
|
|
||||||
document.querySelector('a[href="#workflow"]').click();
|
|
||||||
|
|
||||||
// Prompt in Workflow-Eingabefeld setzen
|
|
||||||
const promptInput = document.getElementById('prompt-input');
|
|
||||||
const promptSelect = document.getElementById('prompt-select');
|
|
||||||
|
|
||||||
if (promptInput) {
|
|
||||||
promptInput.value = selectedPrompt.content;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promptSelect) {
|
|
||||||
// Wenn der Prompt bereits existiert, ihn in der Dropdown auswählen
|
|
||||||
const existingOption = Array.from(promptSelect.options).find(
|
|
||||||
option => option.textContent.includes(selectedPrompt.content.substring(0, 50))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingOption) {
|
|
||||||
promptSelect.value = existingOption.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// "Löschen"-Button
|
|
||||||
document.querySelectorAll('.delete-prompt-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', async () => {
|
|
||||||
const promptId = btn.getAttribute('data-id');
|
|
||||||
|
|
||||||
if (confirm('Möchten Sie diesen Prompt wirklich löschen?')) {
|
|
||||||
try {
|
|
||||||
// Aus dem globalen State entfernen
|
|
||||||
globalState.availablePrompts = globalState.availablePrompts.filter(p => p.id !== promptId);
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyPrompts(globalState);
|
|
||||||
|
|
||||||
// Optional: Prompt vom Server löschen
|
|
||||||
await deletePrompt(promptId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Löschen des Prompts:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt speichern
|
|
||||||
async function savePrompt(promptData, globalState) {
|
|
||||||
try {
|
|
||||||
// Prompt auf dem Server speichern
|
|
||||||
const response = await window.globalUtils.postData('/api/prompts/save', promptData);
|
|
||||||
|
|
||||||
// Rückmeldung vom Server verarbeiten
|
|
||||||
if (response && response.id) {
|
|
||||||
// Wenn der Server eine ID zurückgibt, diese aktualisieren
|
|
||||||
const index = globalState.availablePrompts.findIndex(p => p.id === promptData.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
globalState.availablePrompts[index].id = response.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI aktualisieren
|
|
||||||
renderMyPrompts(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Speichern des Prompts:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt löschen
|
|
||||||
async function deletePrompt(promptId) {
|
|
||||||
try {
|
|
||||||
// Prompt vom Server löschen
|
|
||||||
await window.globalUtils.postData('/api/prompts/delete', { id: promptId });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Löschen des Prompts:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modul-Initialisierung exportieren
|
|
||||||
window.initPromptsModule = initPromptsModule;
|
|
||||||
})();
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
<!-- Workflow Module -->
|
|
||||||
<div class="workflow-container">
|
|
||||||
<!-- Linke Seite - Workflow-Konfiguration -->
|
|
||||||
<div class="config-panel">
|
|
||||||
<h2>Workflow-Konfiguration</h2>
|
|
||||||
|
|
||||||
<!-- 1. Dateien auswählen -->
|
|
||||||
<div class="card">
|
|
||||||
<h3>1. Dateien auswählen</h3>
|
|
||||||
|
|
||||||
<div class="files-container">
|
|
||||||
<div class="file-selection">
|
|
||||||
<div class="section-header">
|
|
||||||
<span>Verfügbare Dateien</span>
|
|
||||||
<button class="upload-btn" id="upload-btn">
|
|
||||||
<i class="fas fa-upload"></i> Hochladen
|
|
||||||
</button>
|
|
||||||
<input type="file" id="file-input" multiple hidden>
|
|
||||||
</div>
|
|
||||||
<ul class="file-list" id="available-files">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="selected-files">
|
|
||||||
<h4>Ausgewählte Dateien</h4>
|
|
||||||
<div class="selected-files-container" id="selected-files-container">
|
|
||||||
<div class="empty-state" id="empty-files-state">
|
|
||||||
<i class="fas fa-upload"></i>
|
|
||||||
<span>Keine Dateien ausgewählt</span>
|
|
||||||
</div>
|
|
||||||
<ul class="selected-file-list" id="selected-files">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Prompt eingeben oder auswählen -->
|
|
||||||
<div class="card">
|
|
||||||
<h3>2. Prompt eingeben oder auswählen</h3>
|
|
||||||
|
|
||||||
<!-- Prompt-Auswahl -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="prompt-select" class="prompt-select-label">
|
|
||||||
Gespeicherte Prompts:
|
|
||||||
</label>
|
|
||||||
<select id="prompt-select" class="prompt-select">
|
|
||||||
<option value="">-- Prompt auswählen --</option>
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Eingabefeld für eigenen Prompt -->
|
|
||||||
<textarea
|
|
||||||
id="prompt-input"
|
|
||||||
placeholder="Beschreiben Sie die Aufgabe für die Agenten..."
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 3. Agenten auswählen -->
|
|
||||||
<div class="card">
|
|
||||||
<h3>3. Agenten auswählen</h3>
|
|
||||||
<ul class="agent-list" id="agent-list">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Aktionsbuttons -->
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="reset-btn" id="reset-btn">Zurücksetzen</button>
|
|
||||||
<button class="start-btn" id="start-workflow-btn">Workflow starten</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Rechte Seite - Ausführung und Ergebnisse -->
|
|
||||||
<div class="results-panel">
|
|
||||||
<h2>Ausführung & Ergebnisse</h2>
|
|
||||||
|
|
||||||
<!-- Ausführungsprotokoll -->
|
|
||||||
<div class="card">
|
|
||||||
<h3>Ausführungsprotokoll</h3>
|
|
||||||
<div class="log-container" id="execution-log">
|
|
||||||
<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Ergebnisse -->
|
|
||||||
<div class="card">
|
|
||||||
<h3>Ergebnisse</h3>
|
|
||||||
<div class="results-container" id="results-container">
|
|
||||||
<div class="results-empty-state" id="empty-results-state">
|
|
||||||
<div>Keine Ergebnisse verfügbar</div>
|
|
||||||
<div class="sub-text">Führen Sie einen Workflow aus, um Ergebnisse zu sehen</div>
|
|
||||||
</div>
|
|
||||||
<div class="results-list" id="results-list">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,408 +0,0 @@
|
||||||
// workflow-module.js
|
|
||||||
(function() {
|
|
||||||
// Workflow-Modul-Zustand
|
|
||||||
let workflowState = {
|
|
||||||
selectedFiles: [],
|
|
||||||
selectedAgents: [],
|
|
||||||
prompt: "",
|
|
||||||
logs: [],
|
|
||||||
results: [],
|
|
||||||
isRunning: false
|
|
||||||
};
|
|
||||||
|
|
||||||
// DOM-Elemente
|
|
||||||
let availableFiles, selectedFiles, emptyFilesState,
|
|
||||||
fileInput, uploadBtn, promptInput, promptSelect,
|
|
||||||
agentList, resetBtn, startWorkflowBtn,
|
|
||||||
executionLog, resultsContainer,
|
|
||||||
emptyResultsState, resultsList;
|
|
||||||
|
|
||||||
// Initialisierungsfunktion für das Workflow-Modul
|
|
||||||
function initWorkflowModule(globalState) {
|
|
||||||
// DOM-Elemente referenzieren
|
|
||||||
availableFiles = document.getElementById('available-files');
|
|
||||||
selectedFiles = document.getElementById('selected-files');
|
|
||||||
emptyFilesState = document.getElementById('empty-files-state');
|
|
||||||
fileInput = document.getElementById('file-input');
|
|
||||||
uploadBtn = document.getElementById('upload-btn');
|
|
||||||
promptInput = document.getElementById('prompt-input');
|
|
||||||
promptSelect = document.getElementById('prompt-select');
|
|
||||||
agentList = document.getElementById('agent-list');
|
|
||||||
resetBtn = document.getElementById('reset-btn');
|
|
||||||
startWorkflowBtn = document.getElementById('start-workflow-btn');
|
|
||||||
executionLog = document.getElementById('execution-log');
|
|
||||||
resultsContainer = document.getElementById('results-container');
|
|
||||||
emptyResultsState = document.getElementById('empty-results-state');
|
|
||||||
resultsList = document.getElementById('results-list');
|
|
||||||
|
|
||||||
// Event-Listener hinzufügen
|
|
||||||
setupEventListeners(globalState);
|
|
||||||
|
|
||||||
// Initialdaten rendern
|
|
||||||
renderFiles(globalState);
|
|
||||||
renderPromptSelector(globalState);
|
|
||||||
renderAgents(globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener einrichten
|
|
||||||
function setupEventListeners(globalState) {
|
|
||||||
// Datei-Upload
|
|
||||||
uploadBtn.addEventListener('click', () => fileInput.click());
|
|
||||||
fileInput.addEventListener('change',
|
|
||||||
window.globalUtils.handleFileUpload(fileInput, (newFile) => {
|
|
||||||
// Nach erfolgreichem Upload
|
|
||||||
workflowState.selectedFiles.push(newFile);
|
|
||||||
renderSelectedFiles();
|
|
||||||
renderFiles(globalState);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prompt-Eingabe
|
|
||||||
promptInput.addEventListener('input', (e) => {
|
|
||||||
workflowState.prompt = e.target.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Prompt-Auswahl
|
|
||||||
promptSelect.addEventListener('change', () => {
|
|
||||||
const selectedPromptId = promptSelect.value;
|
|
||||||
if (selectedPromptId) {
|
|
||||||
const selectedPrompt = globalState.availablePrompts.find(p => p.id === selectedPromptId);
|
|
||||||
if (selectedPrompt) {
|
|
||||||
promptInput.value = selectedPrompt.content;
|
|
||||||
workflowState.prompt = selectedPrompt.content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Zurücksetzen
|
|
||||||
resetBtn.addEventListener('click', resetWorkflow);
|
|
||||||
|
|
||||||
// Workflow starten
|
|
||||||
startWorkflowBtn.addEventListener('click', () => runWorkflow(globalState));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verfügbare Dateien rendern
|
|
||||||
function renderFiles(globalState) {
|
|
||||||
availableFiles.innerHTML = '';
|
|
||||||
globalState.availableFiles.forEach(file => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = 'file-item';
|
|
||||||
const isSelected = workflowState.selectedFiles.some(f => f.id === file.id);
|
|
||||||
li.innerHTML = `
|
|
||||||
<div class="file-item-name">
|
|
||||||
<input type="checkbox" id="file-${file.id}" ${isSelected ? 'checked' : ''}>
|
|
||||||
<label for="file-${file.id}">
|
|
||||||
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
|
|
||||||
${file.name}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<span class="file-item-info">${file.size || ''}</span>
|
|
||||||
`;
|
|
||||||
const checkbox = li.querySelector(`#file-${file.id}`);
|
|
||||||
checkbox.addEventListener('change', () => {
|
|
||||||
if (checkbox.checked) {
|
|
||||||
workflowState.selectedFiles.push(file);
|
|
||||||
} else {
|
|
||||||
workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== file.id);
|
|
||||||
}
|
|
||||||
renderSelectedFiles();
|
|
||||||
});
|
|
||||||
availableFiles.appendChild(li);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ausgewählte Dateien rendern
|
|
||||||
function renderSelectedFiles() {
|
|
||||||
if (workflowState.selectedFiles.length === 0) {
|
|
||||||
emptyFilesState.style.display = 'flex';
|
|
||||||
selectedFiles.innerHTML = '';
|
|
||||||
} else {
|
|
||||||
emptyFilesState.style.display = 'none';
|
|
||||||
selectedFiles.innerHTML = '';
|
|
||||||
workflowState.selectedFiles.forEach(file => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = 'selected-file-item';
|
|
||||||
li.innerHTML = `
|
|
||||||
<div class="file-item-name">
|
|
||||||
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
|
|
||||||
${file.name}
|
|
||||||
</div>
|
|
||||||
<button class="remove-file-btn" data-id="${file.id}">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
selectedFiles.appendChild(li);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event-Listener für Entfernen-Buttons
|
|
||||||
document.querySelectorAll('.remove-file-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const fileId = btn.getAttribute('data-id');
|
|
||||||
workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== fileId);
|
|
||||||
renderSelectedFiles();
|
|
||||||
renderFiles(window.globalState);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt-Auswahlbox rendern
|
|
||||||
function renderPromptSelector(globalState) {
|
|
||||||
// Existierende Optionen entfernen (außer der ersten)
|
|
||||||
while (promptSelect.options.length > 1) {
|
|
||||||
promptSelect.remove(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompts aus dem globalen State hinzufügen
|
|
||||||
globalState.availablePrompts.forEach(prompt => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = prompt.id;
|
|
||||||
option.textContent = prompt.content.substring(0, 50) + (prompt.content.length > 50 ? '...' : '');
|
|
||||||
promptSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agenten rendern
|
|
||||||
function renderAgents(globalState) {
|
|
||||||
agentList.innerHTML = '';
|
|
||||||
|
|
||||||
// Agenten für den aktuellen Workspace filtern
|
|
||||||
const workspaceAgents = globalState.availableAgents.filter(
|
|
||||||
agent => agent.workspace_id === globalState.currentWorkspace?.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (workspaceAgents.length === 0) {
|
|
||||||
agentList.innerHTML = '<div class="empty-state">Keine Agenten im ausgewählten Workspace verfügbar.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
workspaceAgents.forEach(agent => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = 'agent-item';
|
|
||||||
const isSelected = workflowState.selectedAgents.some(a => a.id === agent.id);
|
|
||||||
li.innerHTML = `
|
|
||||||
<div class="agent-header">
|
|
||||||
<input type="checkbox" id="agent-${agent.id}" class="agent-checkbox" ${isSelected ? 'checked' : ''}>
|
|
||||||
<label for="agent-${agent.id}" class="agent-name">${agent.name}</label>
|
|
||||||
</div>
|
|
||||||
<div class="agent-description">${agent.description || ''}</div>
|
|
||||||
`;
|
|
||||||
const checkbox = li.querySelector(`#agent-${agent.id}`);
|
|
||||||
checkbox.addEventListener('change', () => {
|
|
||||||
if (checkbox.checked) {
|
|
||||||
workflowState.selectedAgents.push(agent);
|
|
||||||
} else {
|
|
||||||
workflowState.selectedAgents = workflowState.selectedAgents.filter(a => a.id !== agent.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
agentList.appendChild(li);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ausführungsprotokoll aktualisieren
|
|
||||||
function addLogEntry(message, type = 'info') {
|
|
||||||
const log = {
|
|
||||||
id: `log_${Date.now()}`,
|
|
||||||
message,
|
|
||||||
type,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
};
|
|
||||||
workflowState.logs.push(log);
|
|
||||||
renderLogs();
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Protokoll rendern
|
|
||||||
function renderLogs() {
|
|
||||||
if (workflowState.logs.length === 0) {
|
|
||||||
executionLog.innerHTML = '<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
executionLog.innerHTML = '';
|
|
||||||
workflowState.logs.forEach(log => {
|
|
||||||
const logEntry = document.createElement('div');
|
|
||||||
logEntry.className = 'log-entry';
|
|
||||||
logEntry.innerHTML = `
|
|
||||||
<span class="log-time">[${new Date(log.timestamp).toLocaleTimeString()}]</span>
|
|
||||||
<span class="log-message log-${log.type}">${log.message}</span>
|
|
||||||
`;
|
|
||||||
executionLog.appendChild(logEntry);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Zum Ende scrollen
|
|
||||||
executionLog.scrollTop = executionLog.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ergebnis hinzufügen
|
|
||||||
function addResult(result) {
|
|
||||||
workflowState.results.push(result);
|
|
||||||
renderResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ergebnisse rendern
|
|
||||||
function renderResults() {
|
|
||||||
if (workflowState.results.length === 0) {
|
|
||||||
emptyResultsState.style.display = 'flex';
|
|
||||||
resultsList.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emptyResultsState.style.display = 'none';
|
|
||||||
resultsList.style.display = 'block';
|
|
||||||
resultsList.innerHTML = '';
|
|
||||||
|
|
||||||
workflowState.results.forEach(result => {
|
|
||||||
const resultItem = document.createElement('div');
|
|
||||||
resultItem.className = 'result-item';
|
|
||||||
|
|
||||||
if (result.type === 'text') {
|
|
||||||
resultItem.innerHTML = `
|
|
||||||
<div class="result-header">
|
|
||||||
<div>
|
|
||||||
<div class="result-title">${result.title}</div>
|
|
||||||
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
|
|
||||||
</div>
|
|
||||||
<button class="save-btn">
|
|
||||||
<i class="fas fa-save"></i> Speichern
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="result-content">${result.content}</div>
|
|
||||||
`;
|
|
||||||
} else if (result.type === 'chart') {
|
|
||||||
resultItem.innerHTML = `
|
|
||||||
<div class="result-header">
|
|
||||||
<div>
|
|
||||||
<div class="result-title">${result.title}</div>
|
|
||||||
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
|
|
||||||
</div>
|
|
||||||
<button class="save-btn">
|
|
||||||
<i class="fas fa-save"></i> Speichern
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div class="chart-title">Umsatzentwicklung nach Quartal</div>
|
|
||||||
<div>${result.content}</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
resultsList.appendChild(resultItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workflow-Ausführung
|
|
||||||
async function runWorkflow(globalState) {
|
|
||||||
if (workflowState.selectedFiles.length === 0 ||
|
|
||||||
workflowState.selectedAgents.length === 0 ||
|
|
||||||
!workflowState.prompt.trim()) {
|
|
||||||
alert("Bitte wählen Sie Dateien und Agenten aus und geben Sie einen Prompt ein.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
workflowState.isRunning = true;
|
|
||||||
workflowState.logs = [];
|
|
||||||
workflowState.results = [];
|
|
||||||
|
|
||||||
startWorkflowBtn.textContent = 'Wird ausgeführt...';
|
|
||||||
startWorkflowBtn.classList.add('running');
|
|
||||||
startWorkflowBtn.disabled = true;
|
|
||||||
|
|
||||||
renderLogs();
|
|
||||||
renderResults();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// API-Aufruf an das Backend für Workflow-Ausführung
|
|
||||||
const workflowData = {
|
|
||||||
workspace_id: globalState.currentWorkspace.id,
|
|
||||||
files: workflowState.selectedFiles.map(file => file.id),
|
|
||||||
agents: workflowState.selectedAgents.map(agent => agent.id),
|
|
||||||
prompt: workflowState.prompt,
|
|
||||||
workflow_name: "Workflow " + new Date().toLocaleString()
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await window.globalUtils.postData('/api/workflow/run', workflowData);
|
|
||||||
|
|
||||||
if (response && response.workflow_id) {
|
|
||||||
const workflowId = response.workflow_id;
|
|
||||||
addLogEntry("Workflow gestartet", "info");
|
|
||||||
addLogEntry(`Workflow-ID: ${workflowId}`, "info");
|
|
||||||
|
|
||||||
// Polling für Workflow-Status und -Ergebnisse starten
|
|
||||||
pollWorkflowStatus(workflowId);
|
|
||||||
} else {
|
|
||||||
throw new Error("Workflow konnte nicht gestartet werden");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Starten des Workflows:", error);
|
|
||||||
workflowState.isRunning = false;
|
|
||||||
startWorkflowBtn.textContent = 'Workflow starten';
|
|
||||||
startWorkflowBtn.classList.remove('running');
|
|
||||||
startWorkflowBtn.disabled = false;
|
|
||||||
|
|
||||||
addLogEntry(`Fehler beim Starten des Workflows: ${error.message}`, "error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workflow-Status abfragen
|
|
||||||
async function pollWorkflowStatus(workflowId) {
|
|
||||||
try {
|
|
||||||
// Status abrufen
|
|
||||||
const status = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/status`);
|
|
||||||
|
|
||||||
// Logs abrufen und anzeigen
|
|
||||||
const logs = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/logs`);
|
|
||||||
workflowState.logs = logs;
|
|
||||||
renderLogs();
|
|
||||||
|
|
||||||
// Status aktualisieren
|
|
||||||
if (status) {
|
|
||||||
// Fortschritt anzeigen
|
|
||||||
addLogEntry(`Fortschritt: ${Math.round(status.progress * 100)}%`, "info");
|
|
||||||
|
|
||||||
// Wenn der Workflow abgeschlossen ist, Ergebnisse abrufen
|
|
||||||
if (status.status === 'completed' || status.status === 'failed') {
|
|
||||||
const results = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/results`);
|
|
||||||
workflowState.results = results;
|
|
||||||
renderResults();
|
|
||||||
|
|
||||||
addLogEntry(`Workflow ${status.status === 'completed' ? 'erfolgreich beendet' : 'fehlgeschlagen'}`,
|
|
||||||
status.status === 'completed' ? "success" : "error");
|
|
||||||
|
|
||||||
workflowState.isRunning = false;
|
|
||||||
startWorkflowBtn.textContent = 'Workflow starten';
|
|
||||||
startWorkflowBtn.classList.remove('running');
|
|
||||||
startWorkflowBtn.disabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weiteres Polling nach einer kurzen Verzögerung
|
|
||||||
setTimeout(() => pollWorkflowStatus(workflowId), 2000);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Abrufen des Workflow-Status:", error);
|
|
||||||
addLogEntry(`Fehler beim Abrufen des Workflow-Status: ${error.message}`, "error");
|
|
||||||
|
|
||||||
workflowState.isRunning = false;
|
|
||||||
startWorkflowBtn.textContent = 'Workflow starten';
|
|
||||||
startWorkflowBtn.classList.remove('running');
|
|
||||||
startWorkflowBtn.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workflow zurücksetzen
|
|
||||||
function resetWorkflow() {
|
|
||||||
workflowState.selectedFiles = [];
|
|
||||||
workflowState.selectedAgents = [];
|
|
||||||
promptInput.value = "";
|
|
||||||
promptSelect.selectedIndex = 0;
|
|
||||||
workflowState.prompt = "";
|
|
||||||
|
|
||||||
renderFiles(window.globalState);
|
|
||||||
renderSelectedFiles();
|
|
||||||
renderAgents(window.globalState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modul-Initialisierung exportieren
|
|
||||||
window.initWorkflowModule = initWorkflowModule;
|
|
||||||
})();
|
|
||||||
Binary file not shown.
|
|
@ -1,15 +0,0 @@
|
||||||
<!-- Meine Agents Ansicht -->
|
|
||||||
<div id="agents-view" style="display:none;">
|
|
||||||
<h2>Meine Agents</h2>
|
|
||||||
<div class="card">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>Verfügbare Agents</h3>
|
|
||||||
<button class="add-btn" id="add-agent-btn">
|
|
||||||
<i class="fas fa-plus"></i> Neuen Agent hinzufügen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="agents-list-container">
|
|
||||||
<!-- Wird dynamisch gefüllt -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>PowerOn | Multi-Agent Service</title>
|
|
||||||
<link rel="stylesheet" href="./styles.css">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- Navigation -->
|
|
||||||
<nav class="navbar">
|
|
||||||
<div class="navbar-container">
|
|
||||||
<h1 class="navbar-logo">Data Platform</h1>
|
|
||||||
<div class="navbar-user">
|
|
||||||
<span>Shelly Miller</span>
|
|
||||||
<button class="icon-button">
|
|
||||||
<i class="fas fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="app-container">
|
|
||||||
<!-- Seitenleiste -->
|
|
||||||
<aside class="sidebar">
|
|
||||||
<div class="workspace-section">
|
|
||||||
<h2>Workspaces</h2>
|
|
||||||
<ul class="workspace-list" id="workspace-list">
|
|
||||||
<!-- Workspace-Liste wird vom JavaScript gefüllt -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="sidebar-nav">
|
|
||||||
<li class="sidebar-item active">
|
|
||||||
<a href="#workflow">
|
|
||||||
<i class="fas fa-play-circle"></i>
|
|
||||||
<span>Workflow-Ausführung</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item">
|
|
||||||
<a href="#data">
|
|
||||||
<i class="fas fa-database"></i>
|
|
||||||
<span>Meine Daten</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item">
|
|
||||||
<a href="#prompts">
|
|
||||||
<i class="fas fa-comment-alt"></i>
|
|
||||||
<span>Meine Prompts</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item">
|
|
||||||
<a href="#agents">
|
|
||||||
<i class="fas fa-wrench"></i>
|
|
||||||
<span>Meine Agents</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Hauptinhalt -->
|
|
||||||
<main class="main-content">
|
|
||||||
<!-- Module werden dynamisch geladen -->
|
|
||||||
<div id="part-workflow"></div>
|
|
||||||
<div id="part-data"></div>
|
|
||||||
<div id="part-prompts"></div>
|
|
||||||
<div id="part-agents"></div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Module scripts -->
|
|
||||||
<script src="main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
321
frontend/main.js
321
frontend/main.js
|
|
@ -1,321 +0,0 @@
|
||||||
document.addEventListener('DOMContentLoaded',async function(){
|
|
||||||
// Globale Hilfsfunktionen und Initialisierung
|
|
||||||
|
|
||||||
// State-Management
|
|
||||||
const globalState = {
|
|
||||||
workspaces: [],
|
|
||||||
currentWorkspace: null,
|
|
||||||
availableFiles: [],
|
|
||||||
availableAgents: [],
|
|
||||||
availablePrompts: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
async function loadModuleHtml(filename) {
|
|
||||||
// Backend-URL für HTML Module verwenden
|
|
||||||
try {
|
|
||||||
const apiBaseUrl = `${window.location.protocol}//${window.location.hostname}:8000`;
|
|
||||||
const modulePath = `${apiBaseUrl}/webparts/${filename}`;
|
|
||||||
console.log(`Lade HTML-Modul von: ${modulePath}`);
|
|
||||||
const response = await fetch(modulePath);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP-Fehler beim Laden des Moduls: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const htmlContent = await response.text();
|
|
||||||
return htmlContent;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Fehler beim Laden des Moduls ${filename}:`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadModuleJs(filename) {
|
|
||||||
try {
|
|
||||||
const apiBaseUrl = `${window.location.protocol}//${window.location.hostname}:8000`;
|
|
||||||
const response = await fetch(`${apiBaseUrl}/webparts/${filename}`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Failed to load JS module: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const jsContent = await response.text();
|
|
||||||
// Create a script element and add it to the page
|
|
||||||
const scriptElement = document.createElement('script');
|
|
||||||
scriptElement.textContent = jsContent;
|
|
||||||
document.head.appendChild(scriptElement);
|
|
||||||
console.log(`JS module ${filename} loaded successfully`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error loading JS module ${filename}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchFromApi(url) {
|
|
||||||
// Basis-URL für die API mit korrektem Port
|
|
||||||
const apiBaseUrl = `${window.location.protocol}//${window.location.hostname}:8000`;
|
|
||||||
const fullUrl = url.startsWith('http') ? url : `${apiBaseUrl}${url}`;
|
|
||||||
console.log(`Fetching data from: ${fullUrl}`);
|
|
||||||
return fullUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hilfsfunktion zum Abrufen von Daten vom Backend
|
|
||||||
async function fetchData(url) {
|
|
||||||
try {
|
|
||||||
fullUrl=fetchFromApi(url)
|
|
||||||
const response = await fetch(fullUrl);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Format-Fehler beim Abrufen von ${fullUrl}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Genereller Fehler beim Abrufen von ${fullUrl}:`, error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hilfsfunktion zum Senden von Daten an das Backend
|
|
||||||
async function postData(url, data) {
|
|
||||||
try {
|
|
||||||
fullUrl=fetchFromApi(url)
|
|
||||||
const response = await fetch(fullUrl, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Fehler beim Senden an ${fullUrl}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Fehler beim Senden an ${fullUrl}:`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Globale Initialisierungsfunktion
|
|
||||||
async function initialize() {
|
|
||||||
try {
|
|
||||||
// Load Module Scripts
|
|
||||||
loadModuleJs("part-workflow.js")
|
|
||||||
loadModuleJs("part-data.js")
|
|
||||||
loadModuleJs("part-prompts.js")
|
|
||||||
loadModuleJs("part-agents.js")
|
|
||||||
|
|
||||||
// Daten vom Backend abrufen
|
|
||||||
globalState.workspaces = await fetchData('/api/workspaces');
|
|
||||||
globalState.availableFiles = await fetchData('/api/files');
|
|
||||||
globalState.availablePrompts = await fetchData('/api/prompts');
|
|
||||||
|
|
||||||
// Wenn Workspaces vorhanden sind, den ersten auswählen
|
|
||||||
if (globalState.workspaces.length > 0) {
|
|
||||||
globalState.currentWorkspace = globalState.workspaces[0];
|
|
||||||
|
|
||||||
// Agenten für den aktuellen Workspace abrufen
|
|
||||||
globalState.availableAgents = await fetchData(`/api/agents?workspace_id=${globalState.currentWorkspace.id}`);
|
|
||||||
console.info("Debug agents set for: "+`/api/agents?workspace_id=${globalState.currentWorkspace.id}`+", result="+globalState.availableAgents)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event-Listener für Menüpunkte
|
|
||||||
setupMenuNavigation();
|
|
||||||
|
|
||||||
// Workspaces rendern, nachdem alle Daten geladen wurden
|
|
||||||
renderWorkspaces();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Initialisieren der Daten:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Menünavigation einrichten
|
|
||||||
function setupMenuNavigation() {
|
|
||||||
const menuItems = {
|
|
||||||
'workflow': document.querySelector('a[href="#workflow"]').parentElement,
|
|
||||||
'data': document.querySelector('a[href="#data"]').parentElement,
|
|
||||||
'prompts': document.querySelector('a[href="#prompts"]').parentElement,
|
|
||||||
'agents': document.querySelector('a[href="#agents"]').parentElement
|
|
||||||
};
|
|
||||||
|
|
||||||
const moduleContainers = {
|
|
||||||
'workflow': document.getElementById('part-workflow'),
|
|
||||||
'data': document.getElementById('part-data'),
|
|
||||||
'prompts': document.getElementById('part-prompts'),
|
|
||||||
'agents': document.getElementById('part-agents')
|
|
||||||
};
|
|
||||||
|
|
||||||
// Menüpunkte initialisieren
|
|
||||||
Object.keys(menuItems).forEach(key => {
|
|
||||||
menuItems[key].addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Aktive Klasse von allen Menüpunkten entfernen
|
|
||||||
Object.values(menuItems).forEach(item => item.classList.remove('active'));
|
|
||||||
|
|
||||||
// Alle Module ausblenden
|
|
||||||
Object.values(moduleContainers).forEach(container => container.style.display = 'none')
|
|
||||||
|
|
||||||
// Aktuelle Ansicht aktivieren
|
|
||||||
this.classList.add('active');
|
|
||||||
moduleContainers[key].style.display = 'block';
|
|
||||||
|
|
||||||
// Laden und Aktualisieren der Moduldaten
|
|
||||||
switch(key) {
|
|
||||||
case 'workflow':
|
|
||||||
loadWorkflowModule();
|
|
||||||
break;
|
|
||||||
case 'data':
|
|
||||||
loadDataModule();
|
|
||||||
break;
|
|
||||||
case 'prompts':
|
|
||||||
loadPromptsModule();
|
|
||||||
break;
|
|
||||||
case 'agents':
|
|
||||||
loadAgentsModule();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial das Workflow-Modul laden
|
|
||||||
loadWorkflowModule();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modul-Ladungsfunktionen
|
|
||||||
async function loadWorkflowModule() {
|
|
||||||
try {
|
|
||||||
// Dynamisches Laden des Workflow-Moduls
|
|
||||||
const moduleContent = await loadModuleHtml('part-workflow.html');
|
|
||||||
const workflowModule = document.getElementById('part-workflow');
|
|
||||||
workflowModule.innerHTML = moduleContent;
|
|
||||||
|
|
||||||
// Initialisiere Workflow-Modul-Funktionalität
|
|
||||||
if (window.initWorkflowModule) {
|
|
||||||
window.initWorkflowModule(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Laden des Workflow-Moduls:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadDataModule() {
|
|
||||||
try {
|
|
||||||
// Dynamisches Laden des Daten-Moduls
|
|
||||||
const moduleContent = await loadModuleHtml('part-data.html');
|
|
||||||
const dataModule = document.getElementById('part-data');
|
|
||||||
dataModule.innerHTML = moduleContent;
|
|
||||||
|
|
||||||
// Initialisiere Daten-Modul-Funktionalität
|
|
||||||
if (window.initDataModule) {
|
|
||||||
window.initDataModule(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Laden des Daten-Moduls:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadPromptsModule() {
|
|
||||||
try {
|
|
||||||
// Dynamisches Laden des Prompts-Moduls
|
|
||||||
const moduleContent = await loadModuleHtml('part-prompts.html');
|
|
||||||
const promptsModule = document.getElementById('part-prompts');
|
|
||||||
promptsModule.innerHTML = moduleContent;
|
|
||||||
|
|
||||||
// Initialisiere Prompts-Modul-Funktionalität
|
|
||||||
if (window.initPromptsModule) {
|
|
||||||
window.initPromptsModule(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Laden des Prompts-Moduls:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadAgentsModule() {
|
|
||||||
try {
|
|
||||||
// Dynamisches Laden des Agenten-Moduls
|
|
||||||
const moduleContent = await loadModuleHtml('part-agents.html');
|
|
||||||
const agentsModule = document.getElementById('part-agents');
|
|
||||||
agentsModule.innerHTML = moduleContent;
|
|
||||||
|
|
||||||
// Initialisiere Agenten-Modul-Funktionalität
|
|
||||||
if (window.initAgentsModule) {
|
|
||||||
window.initAgentsModule(globalState);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Laden des Agenten-Moduls:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workspace-Funktionen
|
|
||||||
function renderWorkspaces() {
|
|
||||||
const workspaceList = document.getElementById('workspace-list');
|
|
||||||
workspaceList.innerHTML = '';
|
|
||||||
|
|
||||||
globalState.workspaces.forEach(workspace => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = `workspace-item ${workspace.id === globalState.currentWorkspace?.id ? 'active' : ''}`;
|
|
||||||
li.innerHTML = `
|
|
||||||
<i class="fas fa-folder"></i>
|
|
||||||
<span>${workspace.name}</span>
|
|
||||||
`;
|
|
||||||
li.addEventListener('click', async () => {
|
|
||||||
globalState.currentWorkspace = workspace;
|
|
||||||
|
|
||||||
// Agenten für den neuen Workspace abrufen
|
|
||||||
globalState.availableAgents = await fetchData(`/api/agents?workspace_id=${workspace.id}`);
|
|
||||||
|
|
||||||
renderWorkspaces();
|
|
||||||
|
|
||||||
// Aktualisiere aktive Module
|
|
||||||
loadWorkflowModule();
|
|
||||||
loadAgentsModule();
|
|
||||||
});
|
|
||||||
workspaceList.appendChild(li);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gemeinsame Utility-Funktionen
|
|
||||||
function handleFileUpload(fileInput, onUploadSuccess) {
|
|
||||||
return async () => {
|
|
||||||
if (fileInput.files.length > 0) {
|
|
||||||
for (const file of fileInput.files) {
|
|
||||||
try {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
|
|
||||||
const response = await fetch('/files/upload', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Fehler beim Hochladen der Datei: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newFile = await response.json();
|
|
||||||
|
|
||||||
if (onUploadSuccess) {
|
|
||||||
onUploadSuccess(newFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Globale Dateiliste aktualisieren
|
|
||||||
globalState.availableFiles.push(newFile);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Fehler beim Hochladen der Datei:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eingabefeld zurücksetzen
|
|
||||||
fileInput.value = "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisierung starten
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
// Globale Funktionen für Module exportieren
|
|
||||||
window.globalUtils = {
|
|
||||||
fetchData,
|
|
||||||
postData,
|
|
||||||
handleFileUpload,
|
|
||||||
renderWorkspaces
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
@ -1,796 +0,0 @@
|
||||||
/* Grundlegende Resets und Fonts */
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background-color: #f0f2f5;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navbar */
|
|
||||||
.navbar {
|
|
||||||
background-color: #2563eb;
|
|
||||||
color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 1800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-logo {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-user {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 2rem;
|
|
||||||
height: 2rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: white;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* App Container Layout */
|
|
||||||
.app-container {
|
|
||||||
display: flex;
|
|
||||||
max-width: 1800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
min-height: calc(100vh - 4rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
.sidebar {
|
|
||||||
width: 250px;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
|
|
||||||
padding: 1.5rem 1rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-section h2 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-list {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-item {
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-item i {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
color: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-item:hover {
|
|
||||||
background-color: #f3f4f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-item.active {
|
|
||||||
background-color: #e0f2fe;
|
|
||||||
color: #1d4ed8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-nav {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-item {
|
|
||||||
padding: 0.75rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-item a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-item i {
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
width: 1.25rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-item:hover {
|
|
||||||
background-color: #f3f4f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-item.active {
|
|
||||||
background-color: #e0f2fe;
|
|
||||||
color: #1d4ed8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main Content */
|
|
||||||
.main-content {
|
|
||||||
flex: 1;
|
|
||||||
padding: 1.5rem;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-panel, .results-panel {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cards */
|
|
||||||
.card {
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
padding: 1.25rem;
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
color: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File Selection */
|
|
||||||
.files-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn {
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.375rem 0.75rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #4b5563;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.375rem;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-btn:hover {
|
|
||||||
background-color: #f9fafb;
|
|
||||||
border-color: #9ca3af;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list {
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
background-color: #f9fafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item {
|
|
||||||
padding: 0.625rem;
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item-name {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item-info {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-files-container {
|
|
||||||
border: 1px dashed #d1d5db;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
min-height: 100px;
|
|
||||||
background-color: #f9fafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100px;
|
|
||||||
color: #9ca3af;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state i {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-file-list {
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-file-item {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-file-item:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-file-btn {
|
|
||||||
color: #ef4444;
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-file-btn:hover {
|
|
||||||
background-color: #fee2e2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prompt input */
|
|
||||||
#prompt-input {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 150px;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
font-family: inherit;
|
|
||||||
resize: vertical;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#prompt-input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #2563eb;
|
|
||||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Agent list */
|
|
||||||
.agent-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-item {
|
|
||||||
background-color: #f9fafb;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-checkbox {
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-name {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-description {
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #6b7280;
|
|
||||||
margin-left: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Action buttons */
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reset-btn {
|
|
||||||
background-color: #f3f4f6;
|
|
||||||
color: #4b5563;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.625rem 1.25rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reset-btn:hover {
|
|
||||||
background-color: #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-btn {
|
|
||||||
background-color: #10b981;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.625rem 1.25rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-btn:hover {
|
|
||||||
background-color: #059669;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-btn.running {
|
|
||||||
background-color: #9ca3af;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Log Container */
|
|
||||||
.log-container {
|
|
||||||
background-color: #111827;
|
|
||||||
color: #34d399;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-empty-state {
|
|
||||||
color: #6b7280;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-entry {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-time {
|
|
||||||
color: #9ca3af;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-message {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-info {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-success {
|
|
||||||
color: #34d399;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-error {
|
|
||||||
color: #f87171;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-start {
|
|
||||||
color: #60a5fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-complete {
|
|
||||||
color: #c084fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Results Container */
|
|
||||||
.results-container {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.results-empty-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: #f9fafb;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 2.5rem 1rem;
|
|
||||||
color: #6b7280;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-text {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #9ca3af;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.results-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-item {
|
|
||||||
background-color: #f9fafb;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 1.125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-agent {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
color: #2563eb;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn:hover {
|
|
||||||
background-color: #e0f2fe;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-content {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
white-space: pre-line;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-container {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
text-align: center;
|
|
||||||
height: 200px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-title {
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles für die Prompt-Auswahl */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-select-label {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #4b5563;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
background-color: white;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles für die Prompt-Liste */
|
|
||||||
.prompt-item {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 1rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-content {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
white-space: pre-line;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
background-color: #f9fafb;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-meta {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #6b7280;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.use-prompt-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
background-color: #10b981;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.use-prompt-btn:hover {
|
|
||||||
background-color: #059669;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-prompt-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
background-color: #ef4444;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-prompt-btn:hover {
|
|
||||||
background-color: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles für die Agenten-Liste */
|
|
||||||
.agent-list-item {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 1rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-header h4 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-type {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
background-color: #e0f2fe;
|
|
||||||
color: #0369a1;
|
|
||||||
padding: 0.125rem 0.375rem;
|
|
||||||
border-radius: 9999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-description {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
color: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-capabilities {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.25rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capability-tag {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
background-color: #f3f4f6;
|
|
||||||
color: #4b5563;
|
|
||||||
padding: 0.125rem 0.375rem;
|
|
||||||
border-radius: 9999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-agent-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
background-color: #3b82f6;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-agent-btn:hover {
|
|
||||||
background-color: #2563eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-agent-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
background-color: #ef4444;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-agent-btn:hover {
|
|
||||||
background-color: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles für die Dateien-Liste */
|
|
||||||
.file-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-file-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 1.75rem;
|
|
||||||
height: 1.75rem;
|
|
||||||
background-color: #3b82f6;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-file-btn:hover {
|
|
||||||
background-color: #2563eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-file-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 1.75rem;
|
|
||||||
height: 1.75rem;
|
|
||||||
background-color: #ef4444;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-file-btn:hover {
|
|
||||||
background-color: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles für Abschnittsüberschriften */
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: 0.375rem 0.75rem;
|
|
||||||
background-color: #10b981;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn:hover {
|
|
||||||
background-color: #059669;
|
|
||||||
}
|
|
||||||
89
readme.md
89
readme.md
|
|
@ -31,47 +31,6 @@ Das Projekt besteht aus zwei Hauptkomponenten:
|
||||||
- `agent_service.py` - Multi-Agent-Orchestrierung
|
- `agent_service.py` - Multi-Agent-Orchestrierung
|
||||||
- `requirements.txt` - Python-Abhängigkeiten
|
- `requirements.txt` - Python-Abhängigkeiten
|
||||||
|
|
||||||
## Hauptfunktionen
|
|
||||||
|
|
||||||
### Workspace-Management
|
|
||||||
- Mehrere Workspaces für verschiedene Projekte
|
|
||||||
- Organisierte Gruppenarbeit mit geteilten Ressourcen
|
|
||||||
|
|
||||||
### Datei-Verarbeitung
|
|
||||||
- Upload verschiedener Dateitypen (PDF, Excel, Bilder, etc.)
|
|
||||||
- Automatische Erkennung und Kategorisierung
|
|
||||||
|
|
||||||
### Agent-Orchestrierung
|
|
||||||
- Kombination verschiedener Agent-Typen:
|
|
||||||
- **Datenanalyse-Agent**: Extrahiert Insights aus strukturierten Daten
|
|
||||||
- **Visualisierungs-Agent**: Erstellt Diagramme und visuelle Darstellungen
|
|
||||||
- **Text-Generator**: Verfasst Berichte und Zusammenfassungen
|
|
||||||
- **Web-Scraper**: Sammelt externe Daten
|
|
||||||
- **Marktanalyse-Agent**: Spezialisiert auf Wettbewerbsanalyse
|
|
||||||
|
|
||||||
### Workflow-Ausführung
|
|
||||||
- Echtzeit-Protokollierung des Fortschritts
|
|
||||||
- Vollständige Nachverfolgbarkeit aller Schritte
|
|
||||||
|
|
||||||
### Ergebnis-Management
|
|
||||||
- Strukturierte Darstellung von Analysen, Diagrammen und Berichten
|
|
||||||
- Export- und Sharing-Funktionen
|
|
||||||
|
|
||||||
## Installation und Einrichtung
|
|
||||||
|
|
||||||
### Voraussetzungen
|
|
||||||
- Python 3.8+
|
|
||||||
- Ein moderner Webbrowser
|
|
||||||
- Optional: Node.js für Entwicklungswerkzeuge
|
|
||||||
|
|
||||||
### Frontend-Installation
|
|
||||||
1. Klonen des Repositories
|
|
||||||
2. Platzieren der Frontend-Dateien auf einem Webserver oder lokalen Entwicklungsserver:
|
|
||||||
```bash
|
|
||||||
# Mit Python einen einfachen HTTP-Server starten
|
|
||||||
python -m http.server 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backend-Installation
|
### Backend-Installation
|
||||||
1. Virtuelle Umgebung erstellen und aktivieren:
|
1. Virtuelle Umgebung erstellen und aktivieren:
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -95,54 +54,6 @@ Das Projekt besteht aus zwei Hauptkomponenten:
|
||||||
- Backend API: `http://localhost:8000`
|
- Backend API: `http://localhost:8000`
|
||||||
- API-Dokumentation: `http://localhost:8000/docs`
|
- API-Dokumentation: `http://localhost:8000/docs`
|
||||||
|
|
||||||
## Verwendung: Der Customer Journey
|
|
||||||
|
|
||||||
### 1. Workspace auswählen oder erstellen
|
|
||||||
- Wählen Sie einen vorhandenen Workspace oder erstellen Sie einen neuen für Ihr Projekt
|
|
||||||
|
|
||||||
### 2. Dateien hochladen
|
|
||||||
- Laden Sie die zu analysierenden Dateien hoch
|
|
||||||
- Das System erkennt automatisch Dateitypen und bereitet sie für die Verarbeitung vor
|
|
||||||
|
|
||||||
### 3. Prompt formulieren
|
|
||||||
- Definieren Sie in natürlicher Sprache, was Sie analysieren möchten
|
|
||||||
- Je präziser Ihre Anweisungen, desto zielgerichteter die Ergebnisse
|
|
||||||
|
|
||||||
### 4. Agenten konfigurieren
|
|
||||||
- Wählen Sie die passenden Agenten für Ihre Aufgabe
|
|
||||||
- Kombinieren Sie Agenten für umfassendere Analysen
|
|
||||||
- Datenanalyse → Visualisierung → Textgenerierung
|
|
||||||
|
|
||||||
### 5. Workflow ausführen
|
|
||||||
- Starten Sie den Workflow und verfolgen Sie die Ausführung in Echtzeit
|
|
||||||
- Im linken Bereich sehen Sie die Konfiguration
|
|
||||||
- Im rechten Bereich werden Protokoll und Ergebnisse angezeigt
|
|
||||||
|
|
||||||
### 6. Ergebnisse verwenden
|
|
||||||
- Sehen Sie Analysen, Diagramme und Berichte ein
|
|
||||||
- Exportieren oder teilen Sie die Ergebnisse
|
|
||||||
- Iterieren Sie bei Bedarf mit angepassten Prompts oder Agent-Konfigurationen
|
|
||||||
|
|
||||||
## Anpassung und Erweiterung
|
|
||||||
|
|
||||||
### Integration mit echten KI-Diensten
|
|
||||||
Die aktuelle Implementierung simuliert die Agent-Verarbeitung. Für eine produktive Nutzung:
|
|
||||||
|
|
||||||
1. Erweitern Sie `agent_service.py` mit Integrationen zu:
|
|
||||||
- OpenAI GPT-Modellen (ChatGPT, GPT-4)
|
|
||||||
- Claude von Anthropic
|
|
||||||
- Eigenentwickelten Modellen mit spezieller Expertise
|
|
||||||
|
|
||||||
2. Implementieren Sie robuste Datei-Parser für:
|
|
||||||
- PDF-Dokumente mit OCR
|
|
||||||
- Excel- und CSV-Verarbeitung
|
|
||||||
- Bild- und Medienanalyse
|
|
||||||
|
|
||||||
3. Ergänzen Sie Authentifizierung und Autorisierung:
|
|
||||||
- Benutzer-Accounts mit Rollenkonzept
|
|
||||||
- API-Schlüsselverwaltung für externe Dienste
|
|
||||||
- Sichere Datenspeicherung
|
|
||||||
|
|
||||||
### Datenbank-Migration
|
### Datenbank-Migration
|
||||||
Für größere Installationen die JSON-basierte Datenbank ersetzen durch:
|
Für größere Installationen die JSON-basierte Datenbank ersetzen durch:
|
||||||
- PostgreSQL für relationale Daten
|
- PostgreSQL für relationale Daten
|
||||||
|
|
|
||||||
34
start.bat
34
start.bat
|
|
@ -1,13 +1,13 @@
|
||||||
@echo off
|
@echo off
|
||||||
echo Data Platform - Multi-Agent Service
|
echo Data Platform - Multi-Agent Service
|
||||||
echo Startskript fuer Frontend und Backend
|
echo Startskript fuer gwserver
|
||||||
echo ----------------------------------------
|
echo ----------------------------------------
|
||||||
|
|
||||||
:: Verzeichnisstruktur erstellen, falls sie nicht existiert
|
:: Verzeichnisstruktur erstellen, falls sie nicht existiert
|
||||||
if not exist backend\data mkdir backend\data
|
if not exist gwserver\data mkdir gwserver\data
|
||||||
if not exist backend\uploads mkdir backend\uploads
|
if not exist gwserver\uploads mkdir gwserver\uploads
|
||||||
if not exist backend\results mkdir backend\results
|
if not exist gwserver\results mkdir gwserver\results
|
||||||
if not exist backend\webparts mkdir backend\webparts
|
if not exist gwserver\webparts mkdir gwserver\webparts
|
||||||
|
|
||||||
:: Prüfen, ob Python installiert ist
|
:: Prüfen, ob Python installiert ist
|
||||||
python --version >nul 2>&1
|
python --version >nul 2>&1
|
||||||
|
|
@ -17,37 +17,27 @@ if %errorlevel% neq 0 (
|
||||||
)
|
)
|
||||||
|
|
||||||
:: Virtuelle Umgebung erstellen, falls sie nicht existiert
|
:: Virtuelle Umgebung erstellen, falls sie nicht existiert
|
||||||
if not exist backend\venv (
|
if not exist gwserver\venv (
|
||||||
echo Erstelle virtuelle Python-Umgebung...
|
echo Erstelle virtuelle Python-Umgebung...
|
||||||
cd backend
|
cd gwserver
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
cd ..
|
cd ..
|
||||||
)
|
)
|
||||||
|
|
||||||
:: Virtuelle Umgebung aktivieren
|
:: Virtuelle Umgebung aktivieren
|
||||||
echo Aktiviere virtuelle Umgebung...
|
echo Aktiviere virtuelle Umgebung...
|
||||||
call backend\venv\Scripts\activate
|
call gwserver\venv\Scripts\activate
|
||||||
|
|
||||||
:: Abhängigkeiten installieren
|
:: Abhängigkeiten installieren
|
||||||
echo Installiere Abhaengigkeiten...
|
echo Installiere Abhaengigkeiten...
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
:: Starte Backend in neuem Fenster
|
:: Starte gwserver in neuem Fenster
|
||||||
echo Starte Backend-Server...
|
echo Starte gwserver-Server...
|
||||||
start cmd /k "cd backend && call venv\Scripts\activate && uvicorn app:app --reload --host 0.0.0.0 --port 8000"
|
start cmd /k "cd gwserver && call venv\Scripts\activate && uvicorn app:app --reload --host 0.0.0.0 --port 8000"
|
||||||
|
|
||||||
:: Kurz warten, um sicherzustellen, dass das Backend startet
|
|
||||||
timeout /t 2 >nul
|
|
||||||
|
|
||||||
:: Starte Frontend-Server in neuem Fenster
|
|
||||||
echo Starte Frontend-Server...
|
|
||||||
start cmd /k "cd frontend && python -m http.server 8080"
|
|
||||||
|
|
||||||
echo ----------------------------------------
|
echo ----------------------------------------
|
||||||
echo Server wurden gestartet!
|
echo Server wurden gestartet!
|
||||||
echo Frontend laeuft auf: http://localhost:8080
|
echo Gateway laeuft auf: http://localhost:8000
|
||||||
echo Backend API laeuft auf: http://localhost:8000
|
|
||||||
echo API-Dokumentation: http://localhost:8000/docs
|
echo API-Dokumentation: http://localhost:8000/docs
|
||||||
echo Schliesse die Kommandozeilenfenster, um die Server zu beenden.
|
|
||||||
|
|
||||||
pause
|
pause
|
||||||
43
start.sh
43
start.sh
|
|
@ -6,14 +6,14 @@ BLUE='\033[0;34m'
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
echo -e "${GREEN}Data Platform - Multi-Agent Service${NC}"
|
echo -e "${GREEN}Data Platform - Multi-Agent Service${NC}"
|
||||||
echo -e "${BLUE}Startskript für Frontend und Backend${NC}"
|
echo -e "${BLUE}Startskript für gwserver${NC}"
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
|
|
||||||
# Verzeichnisstruktur erstellen, falls sie nicht existiert
|
# Verzeichnisstruktur erstellen, falls sie nicht existiert
|
||||||
mkdir -p backend/data
|
mkdir -p gwserver/data
|
||||||
mkdir -p backend/uploads
|
mkdir -p gwserver/uploads
|
||||||
mkdir -p backend/results
|
mkdir -p gwserver/results
|
||||||
mkdir -p backend/webparts
|
mkdir -p gwserver/webparts
|
||||||
|
|
||||||
# Prüfen, ob Python installiert ist
|
# Prüfen, ob Python installiert ist
|
||||||
if command -v python3 &>/dev/null; then
|
if command -v python3 &>/dev/null; then
|
||||||
|
|
@ -26,50 +26,37 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Virtuelle Umgebung erstellen, falls sie nicht existiert
|
# Virtuelle Umgebung erstellen, falls sie nicht existiert
|
||||||
if [ ! -d "backend/venv" ]; then
|
if [ ! -d "gwserver/venv" ]; then
|
||||||
echo "Erstelle virtuelle Python-Umgebung..."
|
echo "Erstelle virtuelle Python-Umgebung..."
|
||||||
cd backend
|
cd gwserver
|
||||||
$PYTHON_CMD -m venv venv
|
$PYTHON_CMD -m venv venv
|
||||||
cd ..
|
cd ..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Virtuelle Umgebung aktivieren
|
# Virtuelle Umgebung aktivieren
|
||||||
echo "Aktiviere virtuelle Umgebung..."
|
echo "Aktiviere virtuelle Umgebung..."
|
||||||
source backend/venv/bin/activate 2>/dev/null || . backend/venv/bin/activate
|
source gwserver/venv/bin/activate 2>/dev/null || . gwserver/venv/bin/activate
|
||||||
|
|
||||||
# Abhängigkeiten installieren
|
# Abhängigkeiten installieren
|
||||||
echo "Installiere Abhängigkeiten..."
|
echo "Installiere Abhängigkeiten..."
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# Backend als Hintergrundprozess starten
|
# gwserver als Hintergrundprozess starten
|
||||||
echo "Starte Backend-Server..."
|
echo "Starte gwserver-Server..."
|
||||||
cd backend
|
cd gwserver
|
||||||
uvicorn app:app --reload --host 0.0.0.0 --port 8000 &
|
uvicorn app:app --reload --host 0.0.0.0 --port 8000 &
|
||||||
BACKEND_PID=$!
|
GWSERVER_PID=$!
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Kurz warten, um sicherzustellen, dass das Backend startet
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Frontend-Server starten
|
|
||||||
echo "Starte Frontend-Server..."
|
|
||||||
cd frontend
|
|
||||||
$PYTHON_CMD -m http.server 8080 &
|
|
||||||
FRONTEND_PID=$!
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
echo -e "${GREEN}Server wurden gestartet!${NC}"
|
echo "gwserver API läuft auf: http://localhost:8000"
|
||||||
echo "Frontend läuft auf: http://localhost:8080"
|
|
||||||
echo "Backend API läuft auf: http://localhost:8000"
|
|
||||||
echo "API-Dokumentation: http://localhost:8000/docs"
|
echo "API-Dokumentation: http://localhost:8000/docs"
|
||||||
echo -e "${BLUE}Drücke STRG+C, um beide Server zu beenden${NC}"
|
echo -e "${BLUE}Drücke STRG+C, um Server zu beenden${NC}"
|
||||||
|
|
||||||
# Funktion zum Beenden der Server bei STRG+C
|
# Funktion zum Beenden der Server bei STRG+C
|
||||||
cleanup() {
|
cleanup() {
|
||||||
echo -e "\n${GREEN}Beende Server...${NC}"
|
echo -e "\n${GREEN}Beende Server...${NC}"
|
||||||
kill $BACKEND_PID
|
kill $GWSERVER_PID
|
||||||
kill $FRONTEND_PID
|
|
||||||
echo "Server wurden beendet"
|
echo "Server wurden beendet"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
55
test/data.py
55
test/data.py
|
|
@ -1,55 +0,0 @@
|
||||||
data_object_model = {
|
|
||||||
"Mandate": {
|
|
||||||
"id": "mandate_001",
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"id": "user_001",
|
|
||||||
"settings": {
|
|
||||||
"id": "setting_001",
|
|
||||||
"preferences": {}
|
|
||||||
},
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"id": "session_001",
|
|
||||||
"timestamp": "2025-03-13T12:00:00Z",
|
|
||||||
"active": True
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"workspaces": [
|
|
||||||
{
|
|
||||||
"id": "workspace_001",
|
|
||||||
"prompts": [
|
|
||||||
{
|
|
||||||
"id": "prompt_001",
|
|
||||||
"content": "",
|
|
||||||
"created_at": "2025-03-13T10:30:00Z"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"agents": [
|
|
||||||
{
|
|
||||||
"id": "agent_001",
|
|
||||||
"type": "assistant",
|
|
||||||
"capabilities": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dataObjectReferences": ["dataobject_001", "dataobject_002"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dataObjects": [
|
|
||||||
{
|
|
||||||
"id": "dataobject_001",
|
|
||||||
"type": "document",
|
|
||||||
"content": {},
|
|
||||||
"metadata": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dataobject_002",
|
|
||||||
"type": "image",
|
|
||||||
"content": {},
|
|
||||||
"metadata": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
# pip install Flask requests gunicorn
|
|
||||||
|
|
||||||
from flask import Flask, request, jsonify
|
|
||||||
import requests
|
|
||||||
import openai
|
|
||||||
|
|
||||||
openai.api_key = "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"
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
# Define the routes for the gateway
|
|
||||||
|
|
||||||
@app.route('/service1/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
|
||||||
def service1_proxy(path):
|
|
||||||
url = f'http://service1/{path}'
|
|
||||||
response = requests.request(
|
|
||||||
method=request.method,
|
|
||||||
url=url,
|
|
||||||
headers=request.headers,
|
|
||||||
data=request.get_data(),
|
|
||||||
cookies=request.cookies,
|
|
||||||
allow_redirects=False
|
|
||||||
)
|
|
||||||
return (response.content, response.status_code, response.headers.items())
|
|
||||||
|
|
||||||
@app.route('/gpt', methods=['POST'])
|
|
||||||
def gpt4_proxy():
|
|
||||||
data = request.json
|
|
||||||
prompt = data.get('prompt', '')
|
|
||||||
model = 'gpt-4'
|
|
||||||
response = openai.ChatCompletion.create(
|
|
||||||
messages=[
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": prompt,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
model=model,
|
|
||||||
)
|
|
||||||
return jsonify({'response': response.choices[0].message.content.strip()})
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0', port=8000)
|
|
||||||
|
|
||||||
# start with: gunicorn -w 4 -b 0.0.0.0:8000 app:app
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import requests
|
|
||||||
|
|
||||||
def test_gateway(prompttext):
|
|
||||||
url = 'https://volucy-gateway-e3d2bzbxdeaaayhz.westeurope-01.azurewebsites.net:8000/gpt'
|
|
||||||
# url = 'http://localhost:8000/gpt'
|
|
||||||
payload = {
|
|
||||||
'prompt': prompttext,
|
|
||||||
}
|
|
||||||
response = requests.post(url, json=payload)
|
|
||||||
if response.status_code == 200:
|
|
||||||
print('Response from GPT:', response.json()['response'])
|
|
||||||
else:
|
|
||||||
print('Failed to get response:', response.status_code, response.text)
|
|
||||||
|
|
||||||
test_gateway("Please give me a python script to summarize all cells in an excel sheet")
|
|
||||||
print("finished")
|
|
||||||
Loading…
Reference in a new issue