Add agent reminders, model selection, task distribution and delete functionality

This commit is contained in:
pdyde 2026-02-20 22:37:58 +01:00
parent 56d9bc2c76
commit 84b2fe3dd7
16 changed files with 759 additions and 74 deletions

32
agent_config.json Normal file
View file

@ -0,0 +1,32 @@
{
"researcher": {
"model": "opencode/big-pickle"
},
"location_manager": {
"model": "opencode/big-pickle"
},
"catering_manager": {
"model": "opencode/big-pickle"
},
"program_manager": {
"model": "opencode/big-pickle"
},
"document_editor": {
"model": "opencode/big-pickle"
},
"tax_advisor": {
"model": "opencode/big-pickle"
},
"musik_rechte_advisor": {
"model": "opencode/big-pickle"
},
"zusammenfasser": {
"model": "opencode/big-pickle"
},
"social_media_manager": {
"model": "opencode/big-pickle"
},
"negotiator": {
"model": "opencode/big-pickle"
}
}

View file

@ -0,0 +1,10 @@
# Erinnerungen - Budget_Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,13 @@
# Erinnerungen - Catering Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
- 2026-02-20 22:02: ich brauche einen durchgetakteten plan für das event. musik, essen etc.... - erledigt
- 2026-02-20 21:57: erstelle einen ablaufplan für unser event... - erledigt
- 2026-02-20 21:53: erstelle mir einen plan für unser Event... - erledigt
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Document Editor
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Location Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Musik Rechte Advisor
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Negotiator
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Program Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Researcher
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Social Media Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Tax Advisor
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -0,0 +1,10 @@
# Erinnerungen - Zusammenfasser
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

291
app.py
View file

@ -18,6 +18,33 @@ from dotenv import load_dotenv
load_dotenv()
# ── Agent Konfiguration ───────────────────────────────────────────────────────
AGENT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'agent_config.json')
def get_agent_config():
"""Lädt die Agentenkonfiguration aus der JSON-Datei."""
if os.path.exists(AGENT_CONFIG_FILE):
try:
with open(AGENT_CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except:
pass
return {}
def get_agent_model(agent_key):
"""Gibt das konfigurierte Modell für einen Agenten zurück."""
config = get_agent_config()
return config.get(agent_key, {}).get('model', 'opencode/big-pickle')
def save_agent_config(agent_key, model):
"""Speichert die Konfiguration für einen Agenten."""
config = get_agent_config()
if agent_key not in config:
config[agent_key] = {}
config[agent_key]['model'] = model
with open(AGENT_CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2)
# ── Email-Journal (SQLite) ──────────────────────────────────────────────────
# Speichert jede gesehene Email mit Message-ID und Verarbeitungsstatus.
# Verhindert, dass Emails verloren gehen wenn die App abstürzt.
@ -186,9 +213,12 @@ def execute_agent_task(agent_key, user_prompt, extra_context=""):
# (--prompt flag gibt leere Antwort, daher alles in eine Message)
combined_message = f"{full_system}\n\n---\n\n{user_prompt}"
# Modell aus Konfiguration holen
model = get_agent_model(agent_key)
try:
result = subprocess.run(
['opencode', 'run', '--format', 'json', combined_message],
['opencode', 'run', '--model', model, '--format', 'json', combined_message],
capture_output=True,
text=True,
timeout=300,
@ -887,8 +917,9 @@ def agent_stream():
yield f"data: {json.dumps({'type': 'processing', 'message': f'{agent_name} arbeitet...'})}\n\n"
try:
model = get_agent_model(selected_agent)
proc = subprocess.Popen(
['opencode', 'run', '--format', 'json', full_prompt],
['opencode', 'run', '--model', model, '--format', 'json', full_prompt],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
@ -958,35 +989,83 @@ def agents():
if os.path.exists(agents_dir):
for agent_name in sorted(os.listdir(agents_dir)):
prompt_file = os.path.join(agents_dir, agent_name, 'systemprompt.md')
if os.path.isdir(os.path.join(agents_dir, agent_name)) and os.path.exists(prompt_file):
agent_path = os.path.join(agents_dir, agent_name)
prompt_file = os.path.join(agent_path, 'systemprompt.md')
reminders_file = os.path.join(agent_path, 'reminders.md')
personality_file = os.path.join(agent_path, 'personality.md')
if os.path.isdir(agent_path):
prompt_content = ''
reminders_content = ''
personality_content = ''
if os.path.exists(prompt_file):
with open(prompt_file, 'r', encoding='utf-8') as f:
prompt_content = f.read()
if os.path.exists(reminders_file):
with open(reminders_file, 'r', encoding='utf-8') as f:
reminders_content = f.read()
else:
reminders_content = '# Erinnerungen - ' + agent_name.title() + '\n\n## Aktuelle Tasks\n-\n\n## Notizen\n- \n\n## Letzte Aktionen\n- '
if os.path.exists(personality_file):
with open(personality_file, 'r', encoding='utf-8') as f:
personality_content = f.read()
agents_list.append({
'name': agent_name,
'prompt': prompt_content
'prompt': prompt_content,
'reminders': reminders_content,
'personality': personality_content
})
edit_agent = request.args.get('edit')
if not edit_agent and agents_list:
return redirect(url_for('agents', edit=agents_list[0]['name']))
edit_prompt = ''
edit_reminders = ''
edit_personality = ''
edit_model = 'opencode/big-pickle'
if edit_agent:
for agent in agents_list:
if agent['name'] == edit_agent:
edit_prompt = agent['prompt']
edit_reminders = agent['reminders']
edit_personality = agent['personality']
break
edit_model = get_agent_model(edit_agent)
if request.method == 'POST':
agent_name = request.form.get('agent_name', '').strip()
prompt_content = request.form.get('prompt_content', '')
reminders_content = request.form.get('reminders_content', '')
personality_content = request.form.get('personality_content', '')
if agent_name and prompt_content is not None:
prompt_file = os.path.join(agents_dir, agent_name, 'systemprompt.md')
if agent_name:
agent_path = os.path.join(agents_dir, agent_name)
if prompt_content is not None:
prompt_file = os.path.join(agent_path, 'systemprompt.md')
with open(prompt_file, 'w', encoding='utf-8') as f:
f.write(prompt_content)
flash(f'System-Prompt für "{agent_name}" gespeichert!', 'success')
return redirect(url_for('agents'))
return render_template('agents.html', agents=AGENTS, agents_list=agents_list, edit_agent=edit_agent, edit_prompt=edit_prompt)
if reminders_content is not None:
reminders_file = os.path.join(agent_path, 'reminders.md')
with open(reminders_file, 'w', encoding='utf-8') as f:
f.write(reminders_content)
if personality_content is not None:
personality_file = os.path.join(agent_path, 'personality.md')
with open(personality_file, 'w', encoding='utf-8') as f:
f.write(personality_content)
flash(f'Daten für "{agent_name}" gespeichert!', 'success')
return redirect(url_for('agents', edit=agent_name))
return render_template('agents.html', agents=AGENTS, agents_list=agents_list, edit_agent=edit_agent, edit_prompt=edit_prompt, edit_reminders=edit_reminders, edit_personality=edit_personality, edit_model=edit_model)
@app.route('/files', methods=['GET', 'POST'])
@ -1196,5 +1275,197 @@ def journal_clear():
return redirect(url_for('settings'))
# ── Task API ────────────────────────────────────────────────────────────────
@app.route('/api/tasks', methods=['GET', 'POST'])
def api_tasks():
"""API zum Erstellen und Abrufen von Tasks."""
if request.method == 'POST':
data = request.get_json()
title = data.get('title', '').strip()
description = data.get('description', '')
assigned_agent = data.get('assigned_agent', '')
agent_key = data.get('agent_key', '')
if not title:
return jsonify({'error': 'Kein Titel übergeben'}), 400
task_id = len(tasks) + 1
while any(t['id'] == task_id for t in tasks):
task_id += 1
new_task = {
'id': task_id,
'title': title,
'description': description,
'assigned_agent': AGENTS.get(assigned_agent, {}).get('name', assigned_agent) if assigned_agent else 'Nicht zugewiesen',
'agent_key': agent_key or assigned_agent,
'status': 'pending',
'created': datetime.now().strftime('%Y-%m-%d %H:%M'),
'type': 'agent_created',
'created_by': agent_key
}
tasks.append(new_task)
return jsonify({'success': True, 'task': new_task})
task_list = list(reversed(tasks))
return jsonify({'tasks': task_list})
@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
def update_task_api(task_id):
"""API zum Aktualisieren eines Tasks."""
data = request.get_json()
new_status = data.get('status')
for task in tasks:
if task['id'] == task_id:
task['status'] = new_status
return jsonify({'success': True, 'task': task})
return jsonify({'error': 'Task nicht gefunden'}), 404
@app.route('/api/agent/<agent_name>/model', methods=['POST'])
def set_agent_model(agent_name):
"""Setzt das Modell für einen Agenten."""
data = request.get_json()
if data and 'model' in data:
save_agent_config(agent_name, data['model'])
return jsonify({'success': True, 'message': f'Modell für {agent_name} gesetzt auf {data["model"]}'})
return jsonify({'error': 'Kein Modell übergeben'}), 400
@app.route('/api/agent/<agent_name>/delete', methods=['DELETE'])
def delete_agent(agent_name):
"""Löscht einen Agenten (den gesamten Ordner)."""
import shutil
agents_dir = os.path.join(os.path.dirname(__file__), 'agents')
agent_path = os.path.join(agents_dir, agent_name)
if not os.path.isdir(agent_path):
return jsonify({'error': 'Agent nicht gefunden'}), 404
try:
shutil.rmtree(agent_path)
config = get_agent_config()
if agent_name in config:
del config[agent_name]
with open(AGENT_CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2)
return jsonify({'success': True, 'message': f'Agent "{agent_name}" wurde gelöscht.'})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/agent/<agent_name>/reminders', methods=['GET', 'POST'])
def agent_reminders(agent_name):
agents_dir = os.path.join(os.path.dirname(__file__), 'agents')
agent_path = os.path.join(agents_dir, agent_name)
reminders_file = os.path.join(agent_path, 'reminders.md')
if not os.path.isdir(agent_path):
return jsonify({'error': 'Agent nicht gefunden'}), 404
if request.method == 'GET':
if os.path.exists(reminders_file):
with open(reminders_file, 'r', encoding='utf-8') as f:
content = f.read()
else:
content = ''
return jsonify({'reminders': content})
if request.method == 'POST':
data = request.get_json()
if data and 'reminders' in data:
with open(reminders_file, 'w', encoding='utf-8') as f:
f.write(data['reminders'])
return jsonify({'success': True, 'message': 'Erinnerungen gespeichert'})
return jsonify({'error': 'Keine Daten übergeben'}), 400
@app.route('/api/orchestrator-distribute', methods=['POST'])
def distribute_tasks():
"""Verteilt Tasks parallel an mehrere Agenten."""
data = request.get_json()
tasks_list = data.get('tasks', [])
selected_agents = data.get('agents', [])
if not tasks_list:
return jsonify({'error': 'Keine Tasks übergeben'}), 400
if not selected_agents:
return jsonify({'error': 'Keine Agenten ausgewählt'}), 400
results = []
def run_task_for_agent(agent_key, task):
response = execute_agent_task(agent_key, task)
reminders_file = os.path.join(os.path.dirname(__file__), 'agents', agent_key, 'reminders.md')
if os.path.exists(reminders_file):
try:
with open(reminders_file, 'r', encoding='utf-8') as f:
content = f.read()
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
new_entry = f"\n- {timestamp}: {task[:100]}... - erledigt"
if '## Letzte Aktionen' in content:
content = content.replace('## Letzte Aktionen', f'## Letzte Aktionen{new_entry}')
else:
content += new_entry
with open(reminders_file, 'w', encoding='utf-8') as f:
f.write(content)
except:
pass
return {
'agent': agent_key,
'task': task,
'response': response[:200] + '...' if len(response) > 200 else response
}
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=len(selected_agents)) as executor:
futures = []
for i, task in enumerate(tasks_list):
agent = selected_agents[i % len(selected_agents)]
future = executor.submit(run_task_for_agent, agent, task)
futures.append(future)
for future in concurrent.futures.as_completed(futures):
try:
results.append(future.result())
except Exception as e:
results.append({'error': str(e)})
for i, task_text in enumerate(tasks_list):
agent_key = selected_agents[i % len(selected_agents)]
agent_info = AGENTS.get(agent_key, {})
agent_name = agent_info.get('name', agent_key)
new_task = {
'id': len(tasks) + 1 + i,
'title': task_text[:80],
'description': 'Automatisch erstellt durch Orchestrator',
'assigned_agent': agent_name,
'status': 'completed',
'created': datetime.now().strftime('%Y-%m-%d %H:%M'),
'type': 'orchestrated',
'agent_key': agent_key
}
tasks.append(new_task)
return jsonify({
'success': True,
'message': 'Tasks verteilt',
'results': results
})
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=5000, threaded=True)

View file

@ -4,22 +4,125 @@
{% 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>
<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="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>
</div>
</div>
<div class="col-md-9">
{% if edit_agent %}
<div class="card">
<div class="card-header bg-warning">
<h5 class="mb-0">Prompt bearbeiten: {{ edit_agent }}</h5>
<h5 class="mb-0">Bearbeiten: {{ edit_agent.replace('_', ' ').title() }}</h5>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('agents') }}">
<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="22" style="font-size:.82rem;line-height:1.5;">{{ edit_prompt }}</textarea>
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>
@ -27,7 +130,6 @@
</form>
</div>
</div>
{% else %}
<div class="card">
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
@ -49,7 +151,7 @@
{% for agent in agents_list %}
<tr>
<td>
<strong>{{ agent.name }}</strong>
<strong>{{ agent.name.replace('_', ' ').title() }}</strong>
</td>
<td>
<small style="color:var(--text-muted);">
@ -72,4 +174,70 @@
</div>
</div>
{% endif %}
</div>
</div>
{% 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 %}

View file

@ -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&#10;Catering planen&#10;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 %}

View file

@ -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 %}