Add agent reminders, model selection, task distribution and delete functionality
This commit is contained in:
parent
56d9bc2c76
commit
84b2fe3dd7
16 changed files with 759 additions and 74 deletions
|
|
@ -4,72 +4,240 @@
|
|||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Agenten-Verwaltung</h1>
|
||||
<p>System-Prompts bearbeiten und verwalten</p>
|
||||
<p>System-Prompts, Erinnerungen und Persönlichkeit bearbeiten</p>
|
||||
</div>
|
||||
|
||||
{% if edit_agent %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning">
|
||||
<h5 class="mb-0">Prompt bearbeiten: {{ edit_agent }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('agents') }}">
|
||||
<input type="hidden" name="agent_name" value="{{ edit_agent }}">
|
||||
<div class="mb-3">
|
||||
<label for="prompt_content" class="form-label">System-Prompt</label>
|
||||
<textarea class="form-control font-monospace" id="prompt_content" name="prompt_content"
|
||||
rows="22" style="font-size:.82rem;line-height:1.5;">{{ edit_prompt }}</textarea>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark">
|
||||
<h5 class="mb-0">Agenten</h5>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
<a href="{{ url_for('agents') }}" class="btn btn-secondary">Abbrechen</a>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for agent in agents_list %}
|
||||
<a href="{{ url_for('agents', edit=agent.name) }}"
|
||||
class="list-group-item list-group-item-action {% if edit_agent == agent.name %}active{% endif %}">
|
||||
<strong>{{ agent.name.replace('_', ' ').title() }}</strong>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Alle Agenten</h5>
|
||||
<span class="badge bg-secondary">{{ agents_list|length }}</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if agents_list %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Agent</th>
|
||||
<th>Prompt-Vorschau</th>
|
||||
<th style="width:120px;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for agent in agents_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ agent.name }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<small style="color:var(--text-muted);">
|
||||
{{ agent.prompt[:160] }}{% if agent.prompt|length > 160 %}…{% endif %}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('agents', edit=agent.name) }}" class="btn btn-sm btn-outline-primary">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="col-md-9">
|
||||
{% if edit_agent %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning">
|
||||
<h5 class="mb-0">Bearbeiten: {{ edit_agent.replace('_', ' ').title() }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('agents', edit=edit_agent) }}">
|
||||
<input type="hidden" name="agent_name" value="{{ edit_agent }}">
|
||||
|
||||
<ul class="nav nav-tabs mb-3" id="agentTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="prompt-tab" data-bs-toggle="tab" data-bs-target="#prompt" type="button">
|
||||
System-Prompt
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="reminders-tab" data-bs-toggle="tab" data-bs-target="#reminders" type="button">
|
||||
Erinnerungen
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="personality-tab" data-bs-toggle="tab" data-bs-target="#personality" type="button">
|
||||
Persönlichkeit
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="model-tab" data-bs-toggle="tab" data-bs-target="#model" type="button">
|
||||
Modell
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link text-danger" id="danger-tab" data-bs-toggle="tab" data-bs-target="#danger" type="button">
|
||||
⚠️ Löschen
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="agentTabContent">
|
||||
<div class="tab-pane fade show active" id="prompt">
|
||||
<div class="mb-3">
|
||||
<label for="prompt_content" class="form-label">System-Prompt</label>
|
||||
<textarea class="form-control font-monospace" id="prompt_content" name="prompt_content"
|
||||
rows="18" style="font-size:.82rem;line-height:1.5;">{{ edit_prompt }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="reminders">
|
||||
<div class="mb-3">
|
||||
<label for="reminders_content" class="form-label">Erinnerungen</label>
|
||||
<div class="form-text mb-2">
|
||||
Hier kannst du manuell Notizen hinzufügen. Agenten können diese Datei auch selbst beschreiben.
|
||||
</div>
|
||||
<textarea class="form-control font-monospace" id="reminders_content" name="reminders_content"
|
||||
rows="18" style="font-size:.82rem;line-height:1.5;">{{ edit_reminders }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="personality">
|
||||
<div class="mb-3">
|
||||
<label for="personality_content" class="form-label">Persönlichkeit (optional)</label>
|
||||
<div class="form-text mb-2">
|
||||
Definiere die Persönlichkeitszüge des Agenten.
|
||||
</div>
|
||||
<textarea class="form-control font-monospace" id="personality_content" name="personality_content"
|
||||
rows="18" style="font-size:.82rem;line-height:1.5;">{{ edit_personality }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="model">
|
||||
<div class="mb-3">
|
||||
<label for="model_select" class="form-label">KI-Modell</label>
|
||||
<div class="form-text mb-2">
|
||||
Wähle das KI-Modell, das dieser Agent verwendet.
|
||||
</div>
|
||||
<select class="form-select" id="model_select" name="model_select" onchange="saveModel('{{ edit_agent }}')">
|
||||
<option value="opencode/big-pickle" {% if edit_model == 'opencode/big-pickle' %}selected{% endif %}>opencode/big-pickle</option>
|
||||
<option value="opencode/gpt-5-nano" {% if edit_model == 'opencode/gpt-5-nano' %}selected{% endif %}>opencode/gpt-5-nano</option>
|
||||
<option value="opencode/glm-5-free" {% if edit_model == 'opencode/glm-5-free' %}selected{% endif %}>opencode/glm-5-free</option>
|
||||
<option value="opencode/minimax-m2.5-free" {% if edit_model == 'opencode/minimax-m2.5-free' %}selected{% endif %}>opencode/minimax-m2.5-free</option>
|
||||
<option value="opencode/trinity-large-preview-free" {% if edit_model == 'opencode/trinity-large-preview-free' %}selected{% endif %}>opencode/trinity-large-preview-free</option>
|
||||
</select>
|
||||
<div id="modelStatus" class="form-text mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="danger">
|
||||
<div class="alert alert-danger">
|
||||
<h5 class="alert-heading">⚠️ Danger Zone</h5>
|
||||
<p class="mb-2">Hier kannst du diesen Agenten dauerhaft löschen. <strong>Diese Aktion kann nicht rückgängig gemacht werden!</strong></p>
|
||||
<p class="mb-3"><small>Alle Dateien im Ordner <code>agents/{{ edit_agent }}/</code> werden gelöscht.</small></p>
|
||||
<button type="button" class="btn btn-danger" onclick="deleteAgent('{{ edit_agent }}')">
|
||||
Agent löschen
|
||||
</button>
|
||||
<div id="deleteStatus" class="form-text mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
<a href="{{ url_for('agents') }}" class="btn btn-secondary">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning m-3">
|
||||
Keine Agenten gefunden. Stelle sicher, dass <code>agents/</code> Unterverzeichnisse mit <code>systemprompt.md</code> enthält.
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Alle Agenten</h5>
|
||||
<span class="badge bg-secondary">{{ agents_list|length }}</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if agents_list %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Agent</th>
|
||||
<th>Prompt-Vorschau</th>
|
||||
<th style="width:120px;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for agent in agents_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ agent.name.replace('_', ' ').title() }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<small style="color:var(--text-muted);">
|
||||
{{ agent.prompt[:160] }}{% if agent.prompt|length > 160 %}…{% endif %}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('agents', edit=agent.name) }}" class="btn btn-sm btn-outline-primary">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning m-3">
|
||||
Keine Agenten gefunden. Stelle sicher, dass <code>agents/</code> Unterverzeichnisse mit <code>systemprompt.md</code> enthält.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function saveModel(agentName) {
|
||||
const select = document.getElementById('model_select');
|
||||
const model = select.value;
|
||||
const status = document.getElementById('modelStatus');
|
||||
|
||||
fetch('/api/agent/' + agentName + '/model', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ model: model })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
status.textContent = '✓ ' + data.message;
|
||||
status.className = 'form-text mt-2 text-success';
|
||||
} else {
|
||||
status.textContent = 'Fehler: ' + data.error;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
status.textContent = 'Fehler: ' + err.message;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAgent(agentName) {
|
||||
if (!confirm('Willst du den Agenten "' + agentName + '" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden!')) {
|
||||
return;
|
||||
}
|
||||
if (!confirm('Sind Sie ABSOLUT sicher? Alle Dateien werden gelöscht!')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const status = document.getElementById('deleteStatus');
|
||||
status.textContent = 'Lösche...';
|
||||
status.className = 'form-text mt-2 text-warning';
|
||||
|
||||
fetch('/api/agent/' + agentName + '/delete', {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
status.textContent = '✓ ' + data.message;
|
||||
status.className = 'form-text mt-2 text-success';
|
||||
setTimeout(() => {
|
||||
window.location.href = '/agents';
|
||||
}, 1500);
|
||||
} else {
|
||||
status.textContent = 'Fehler: ' + data.error;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
status.textContent = 'Fehler: ' + err.message;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,32 @@
|
|||
<div class="row g-4">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Task Distribution -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-info">
|
||||
<h5 class="mb-0">📋 Tasks verteilen</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Aufgaben (eine pro Zeile)</label>
|
||||
<textarea class="form-control" id="todoPrompt" rows="4" placeholder="Recherche Location Catering planen Pressemitteilung schreiben"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Agenten auswählen</label>
|
||||
<div id="agentCheckboxes">
|
||||
{% for key, agent in agents.items() %}
|
||||
<div class="form-check">
|
||||
<input class="form-check-input agent-checkbox" type="checkbox" value="{{ key }}" id="agent_{{ key }}" checked>
|
||||
<label class="form-check-label" for="agent_{{ key }}">{{ agent.name }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-info w-100" onclick="distributeTodos()">Tasks parallel ausführen</button>
|
||||
<div id="todoStatus" class="form-text mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-dark">
|
||||
<h5 class="mb-0">Prompt eingeben</h5>
|
||||
|
|
@ -153,5 +179,42 @@ function sendPromptWithStream() {
|
|||
streamBtn.textContent = 'Live-Antwort anfordern';
|
||||
});
|
||||
}
|
||||
|
||||
function distributeTodos() {
|
||||
const prompt = document.getElementById('todoPrompt').value.trim();
|
||||
if (!prompt) { alert('Bitte Aufgaben eingeben!'); return; }
|
||||
|
||||
const selectedAgents = Array.from(document.querySelectorAll('.agent-checkbox:checked')).map(cb => cb.value);
|
||||
if (selectedAgents.length === 0) { alert('Bitte mindestens einen Agenten auswählen!'); return; }
|
||||
|
||||
const status = document.getElementById('todoStatus');
|
||||
status.textContent = 'Starte parallele Ausführung...';
|
||||
status.className = 'form-text mt-2 text-info';
|
||||
|
||||
const tasks = prompt.split('\n').filter(t => t.trim());
|
||||
|
||||
fetch('/api/orchestrator-distribute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ tasks, agents: selectedAgents })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
status.textContent = '✓ ' + data.message + ' - ' + data.results.length + ' Tasks gestartet';
|
||||
status.className = 'form-text mt-2 text-success';
|
||||
if (data.results && data.results.length > 0) {
|
||||
location.reload();
|
||||
}
|
||||
} else {
|
||||
status.textContent = 'Fehler: ' + data.error;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
status.textContent = 'Fehler: ' + err.message;
|
||||
status.className = 'form-text mt-2 text-danger';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Task-Verwaltung</h1>
|
||||
<p>Manuelle und automatische Email-Tasks</p>
|
||||
<p>Manuelle, orchestrierte und Agent-Tasks</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-success">
|
||||
<h5 class="mb-0">Neuen Task erstellen</h5>
|
||||
</div>
|
||||
|
|
@ -37,6 +37,18 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-info">
|
||||
<h5 class="mb-0">🔄 Auto-Refresh</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="autoRefresh" onchange="toggleAutoRefresh()">
|
||||
<label class="form-check-label" for="autoRefresh">Automatisch aktualisieren (alle 30s)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
|
|
@ -68,6 +80,12 @@
|
|||
{% if task.type == 'email' %}
|
||||
<span class="badge bg-info ms-1" title="Von: {{ task.reply_to }}">Email</span>
|
||||
{% endif %}
|
||||
{% if task.type == 'orchestrated' %}
|
||||
<span class="badge ms-1" style="background-color:#9333ea;">Orchestriert</span>
|
||||
{% endif %}
|
||||
{% if task.type == 'agent_created' %}
|
||||
<span class="badge bg-warning ms-1">Agent</span>
|
||||
{% endif %}
|
||||
{% if task.description %}
|
||||
<div style="font-size:.75rem;color:var(--text-muted);">
|
||||
{{ task.description[:60] }}{% if task.description|length > 60 %}…{% endif %}
|
||||
|
|
@ -116,3 +134,23 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let autoRefreshInterval = null;
|
||||
|
||||
function toggleAutoRefresh() {
|
||||
const checkbox = document.getElementById('autoRefresh');
|
||||
if (checkbox.checked) {
|
||||
autoRefreshInterval = setInterval(() => {
|
||||
location.reload();
|
||||
}, 30000);
|
||||
} else {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue