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
32
agent_config.json
Normal file
32
agent_config.json
Normal 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"
|
||||
}
|
||||
}
|
||||
10
agents/budget_manager/reminders.md
Normal file
10
agents/budget_manager/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Budget_Manager
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
13
agents/catering_manager/reminders.md
Normal file
13
agents/catering_manager/reminders.md
Normal 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
|
||||
-
|
||||
10
agents/document_editor/reminders.md
Normal file
10
agents/document_editor/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Document Editor
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/location_manager/reminders.md
Normal file
10
agents/location_manager/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Location Manager
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/musik_rechte_advisor/reminders.md
Normal file
10
agents/musik_rechte_advisor/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Musik Rechte Advisor
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/negotiator/reminders.md
Normal file
10
agents/negotiator/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Negotiator
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/program_manager/reminders.md
Normal file
10
agents/program_manager/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Program Manager
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/researcher/reminders.md
Normal file
10
agents/researcher/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Researcher
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/social_media_manager/reminders.md
Normal file
10
agents/social_media_manager/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Social Media Manager
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/tax_advisor/reminders.md
Normal file
10
agents/tax_advisor/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Tax Advisor
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
10
agents/zusammenfasser/reminders.md
Normal file
10
agents/zusammenfasser/reminders.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Erinnerungen - Zusammenfasser
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
291
app.py
291
app.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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