feat: Complete DB migration - Command parsing & task persistence

- Add command parsing for @SEND_EMAIL, @SEND_TELEGRAM, @UPDATE_TEAM_MEMBER, @ADD_TEAM_MEMBER
- Migrate all tasks.append() calls (13 occurrences) to use create_task() for DB persistence
- Update task routes to read from database instead of in-memory array
- Orchestrator beat now executes parsed commands (email/telegram notifications)
- Maintain legacy task_queue compatibility for email processing
- All tasks now persist across app restarts
This commit is contained in:
pdyde 2026-02-21 14:13:24 +01:00
parent 42a84141d9
commit 320a1d4d87

390
app.py
View file

@ -884,10 +884,9 @@ def parse_agent_commands(agent_key, response_text):
) )
for question, context in ask_requests: for question, context in ask_requests:
# Erstelle Task für Orchestrator um die Frage zu beantworten # Erstelle Task für Orchestrator um die Frage zu beantworten
new_task = { task_id = create_task(
'id': len(tasks) + 1, title=f"Frage von {agent_key}: {question.strip()[:80]}",
'title': f"Frage von {agent_key}: {question.strip()[:80]}", description=f"""Ein Agent braucht Hilfe!
'description': f"""Ein Agent braucht Hilfe!
**Von:** {agent_key} **Von:** {agent_key}
**Frage:** {question.strip()} **Frage:** {question.strip()}
@ -895,16 +894,13 @@ def parse_agent_commands(agent_key, response_text):
Bitte beantworte die Frage oder delegiere an den passenden Experten-Agent. Bitte beantworte die Frage oder delegiere an den passenden Experten-Agent.
Die Antwort wird an {agent_key} zurückgegeben.""", Die Antwort wird an {agent_key} zurückgegeben.""",
'assigned_agent': 'Orchestrator', agent_key='orchestrator',
'agent_key': 'orchestrator', task_type='agent_question',
'status': 'pending', created_by=agent_key,
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), from_agent=agent_key,
'type': 'agent_question', return_to=agent_key
'from_agent': agent_key, )
'return_to': agent_key logger.info(f"[AgentCmd] {agent_key} fragt Orchestrator (Task #{task_id}): {question.strip()[:50]}")
}
tasks.append(new_task)
logger.info(f"[AgentCmd] {agent_key} fragt Orchestrator: {question.strip()[:50]}")
# CREATE_SUBTASK: Agent möchte Subtask erstellen # CREATE_SUBTASK: Agent möchte Subtask erstellen
subtask_requests = re.findall( subtask_requests = re.findall(
@ -913,19 +909,15 @@ Die Antwort wird an {agent_key} zurückgegeben.""",
re.DOTALL re.DOTALL
) )
for task_desc, requirements in subtask_requests: for task_desc, requirements in subtask_requests:
new_task = { task_id = create_task(
'id': len(tasks) + 1, title=task_desc.strip()[:100],
'title': task_desc.strip()[:100], description=f"Von {agent_key} angefordert:\n{requirements.strip()}",
'description': f"Von {agent_key} angefordert:\n{requirements.strip()}", agent_key='orchestrator',
'assigned_agent': 'Orchestrator', task_type='agent_subtask',
'agent_key': 'orchestrator', created_by=agent_key,
'status': 'pending', from_agent=agent_key
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), )
'type': 'agent_subtask', logger.info(f"[AgentCmd] {agent_key} erstellt Subtask #{task_id} via Orchestrator")
'from_agent': agent_key
}
tasks.append(new_task)
logger.info(f"[AgentCmd] {agent_key} erstellt Subtask via Orchestrator")
# SUGGEST_AGENT: Agent schlägt neuen Agent vor # SUGGEST_AGENT: Agent schlägt neuen Agent vor
suggest_requests = re.findall( suggest_requests = re.findall(
@ -938,28 +930,24 @@ Die Antwort wird an {agent_key} zurückgegeben.""",
agent_key_suggestion = role.lower().replace(' ', '_').replace('-', '_') agent_key_suggestion = role.lower().replace(' ', '_').replace('-', '_')
# Task für Orchestrator um Agent zu erstellen # Task für Orchestrator um Agent zu erstellen
new_task = { task_id = create_task(
'id': len(tasks) + 1, title=f"Agent-Vorschlag: {role.strip()}",
'title': f"Agent-Vorschlag: {role.strip()}", description=f"""Agent {agent_key} schlägt vor, einen neuen Agent zu erstellen:
'description': f"""Agent {agent_key} schlägt vor, einen neuen Agent zu erstellen:
**Rolle:** {role.strip()} **Rolle:** {role.strip()}
**Fähigkeiten:** {skills.strip()} **Fähigkeiten:** {skills.strip()}
**Begründung:** {reason.strip()} **Begründung:** {reason.strip()}
Bitte entscheide ob dieser Agent erstellt werden soll.""", Bitte entscheide ob dieser Agent erstellt werden soll.""",
'assigned_agent': 'Orchestrator', agent_key='orchestrator',
'agent_key': 'orchestrator', task_type='agent_suggestion',
'status': 'pending', created_by=agent_key,
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), from_agent=agent_key,
'type': 'agent_suggestion', suggested_agent=agent_key_suggestion,
'from_agent': agent_key, suggested_role=role.strip(),
'suggested_agent': agent_key_suggestion, suggested_skills=skills.strip()
'suggested_role': role.strip(), )
'suggested_skills': skills.strip() logger.info(f"[AgentCmd] {agent_key} schlägt neuen Agent vor (Task #{task_id}): {role.strip()}")
}
tasks.append(new_task)
logger.info(f"[AgentCmd] {agent_key} schlägt neuen Agent vor: {role.strip()}")
# READ_KNOWLEDGE: Agent möchte Wissensdatenbank durchsuchen # READ_KNOWLEDGE: Agent möchte Wissensdatenbank durchsuchen
read_kb_requests = re.findall( read_kb_requests = re.findall(
@ -990,6 +978,124 @@ Bitte entscheide ob dieser Agent erstellt werden soll.""",
if relevant_sections: if relevant_sections:
logger.info(f"[AgentCmd] {len(relevant_sections)} relevante Zeilen gefunden") logger.info(f"[AgentCmd] {len(relevant_sections)} relevante Zeilen gefunden")
# SEND_EMAIL: Orchestrator sendet Email an Team-Member
send_email_requests = re.findall(
r'@SEND_EMAIL\s*\nTo:\s*([^\n]+)\s*\nSubject:\s*([^\n]+)\s*\nBody:\s*([^@]+)@END',
response_text,
re.DOTALL
)
for to_addr, subject, body in send_email_requests:
to_clean = to_addr.strip()
subject_clean = subject.strip()
body_clean = body.strip()
# Versuche Email zu senden
success, message = send_email(to_clean, subject_clean, body_clean)
if success:
logger.info(f"[AgentCmd] Email gesendet an {to_clean}: {subject_clean}")
else:
logger.error(f"[AgentCmd] Email-Fehler: {message}")
# SEND_TELEGRAM: Orchestrator sendet Telegram-Nachricht
send_telegram_requests = re.findall(
r'@SEND_TELEGRAM\s*\nTo:\s*([^\n]+)\s*\nMessage:\s*([^@]+)@END',
response_text,
re.DOTALL
)
for recipient, message in send_telegram_requests:
recipient_clean = recipient.strip()
message_clean = message.strip()
# Telegram-Integration (wenn aktiviert)
if TELEGRAM_CONFIG.get('bot_token') and TELEGRAM_CONFIG.get('telegram_bot'):
try:
# Finde Chat-ID für Recipient (basierend auf Team-Member)
con = sqlite3.connect(EMAIL_JOURNAL_DB)
result = con.execute(
"SELECT telegram_chat_id FROM team_members WHERE name = ? OR email = ?",
(recipient_clean, recipient_clean)
).fetchone()
con.close()
if result and result[0]:
chat_id = result[0]
import asyncio
asyncio.run(TELEGRAM_CONFIG['telegram_bot'].bot.send_message(
chat_id=chat_id,
text=message_clean
))
logger.info(f"[AgentCmd] Telegram gesendet an {recipient_clean}")
else:
logger.warning(f"[AgentCmd] Keine Telegram Chat-ID für {recipient_clean}")
except Exception as e:
logger.error(f"[AgentCmd] Telegram-Fehler: {str(e)}")
else:
logger.warning("[AgentCmd] Telegram nicht konfiguriert")
# ADD_TEAM_MEMBER: Füge neues Team-Mitglied hinzu
add_member_requests = re.findall(
r'@ADD_TEAM_MEMBER\s*\nName:\s*([^\n]+)\s*\nEmail:\s*([^\n]+)\s*\nRole:\s*([^\n]+)\s*\nResponsibilities:\s*([^@]+)@END',
response_text,
re.DOTALL
)
for name, email, role, resp in add_member_requests:
name_clean = name.strip()
email_clean = email.strip()
role_clean = role.strip()
resp_clean = resp.strip()
success = add_team_member(name_clean, role_clean, email_clean, resp_clean)
if success:
logger.info(f"[AgentCmd] Team-Member hinzugefügt: {name_clean} ({role_clean})")
else:
logger.warning(f"[AgentCmd] Team-Member konnte nicht hinzugefügt werden: {name_clean}")
# UPDATE_TEAM_MEMBER: Aktualisiere Team-Mitglied
update_member_requests = re.findall(
r'@UPDATE_TEAM_MEMBER\s*\nEmail:\s*([^\n]+)\s*\nField:\s*([^\n]+)\s*\nValue:\s*([^@]+)@END',
response_text,
re.DOTALL
)
for email, field, value in update_member_requests:
email_clean = email.strip()
field_clean = field.strip().lower()
value_clean = value.strip()
# Hole aktuelles Team-Member
con = sqlite3.connect(EMAIL_JOURNAL_DB)
member = con.execute(
"SELECT name, role, email, responsibilities FROM team_members WHERE LOWER(email) = ?",
(email_clean.lower(),)
).fetchone()
if member:
# Update je nach Field
updates = {
'name': member[0],
'role': member[1],
'email': member[2],
'responsibilities': member[3]
}
if field_clean in updates:
updates[field_clean] = value_clean
success = update_team_member(
email_clean,
updates['name'],
updates['role'],
updates['responsibilities']
)
if success:
logger.info(f"[AgentCmd] Team-Member aktualisiert: {email_clean} - {field_clean}")
else:
logger.error(f"[AgentCmd] Update fehlgeschlagen für {email_clean}")
else:
logger.warning(f"[AgentCmd] Unbekanntes Field: {field_clean}")
else:
logger.warning(f"[AgentCmd] Team-Member nicht gefunden: {email_clean}")
con.close()
def create_new_agent(agent_key, role, skills): def create_new_agent(agent_key, role, skills):
"""Erstellt dynamisch einen neuen Agenten.""" """Erstellt dynamisch einen neuen Agenten."""
agent_dir = os.path.join(AGENTS_BASE_DIR, agent_key) agent_dir = os.path.join(AGENTS_BASE_DIR, agent_key)
@ -1342,24 +1448,15 @@ async def telegram_message_handler(update: Update, context: ContextTypes.DEFAULT
return return
# Task erstellen # Task erstellen
task_id = len(tasks) + 1 task_id = create_task(
while any(t['id'] == task_id for t in tasks): title=f"Telegram: {message_text[:50]}{'...' if len(message_text) > 50 else ''}",
task_id += 1 description=message_text,
agent_key='orchestrator',
new_task = { task_type='telegram',
'id': task_id, created_by=f'telegram_user_{user_id}',
'title': f"Telegram: {message_text[:50]}{'...' if len(message_text) > 50 else ''}", telegram_chat_id=update.effective_chat.id,
'description': message_text, telegram_user=username
'assigned_agent': 'Orchestrator', )
'agent_key': 'orchestrator',
'status': 'pending',
'created': datetime.now().strftime('%Y-%m-%d %H:%M'),
'type': 'telegram',
'created_by': f'telegram_user_{user_id}',
'telegram_chat_id': update.effective_chat.id,
'telegram_user': username
}
tasks.append(new_task)
await update.message.reply_text( await update.message.reply_text(
f"✅ Task #{task_id} erstellt!\n\n" f"✅ Task #{task_id} erstellt!\n\n"
@ -1600,9 +1697,26 @@ Diese Email kommt von **{known_member[0]}** ({known_member[1]})
# Journal: Status 'queued' \Seen wird NICHT gesetzt (erst nach Verarbeitung) # Journal: Status 'queued' \Seen wird NICHT gesetzt (erst nach Verarbeitung)
journal_insert(message_id, email_id.decode(), sender, subject, 'queued', agent_key) journal_insert(message_id, email_id.decode(), sender, subject, 'queued', agent_key)
# Task erstellen und in beide Listen eintragen # Task erstellen in DB
task_id = create_task(
title=f"📧 Email: {subject[:50]}",
description=body[:500],
agent_key=agent_key,
task_type='email',
created_by=sender_email,
reply_to=sender_email,
reply_subject=f"Re: {subject}" if not subject.startswith('Re:') else subject,
original_sender=sender,
original_subject=subject,
original_body=body,
message_id=message_id,
imap_uid=email_id.decode(),
extra_context=extra_context
)
# Für task_queue brauchen wir Task als Dict (Legacy-Kompatibilität)
task = { task = {
'id': len(tasks) + 1, 'id': task_id,
'title': f"📧 Email: {subject[:50]}", 'title': f"📧 Email: {subject[:50]}",
'description': body[:500], 'description': body[:500],
'assigned_agent': AGENTS.get(agent_key, {}).get('name', agent_key), 'assigned_agent': AGENTS.get(agent_key, {}).get('name', agent_key),
@ -1615,11 +1729,11 @@ Diese Email kommt von **{known_member[0]}** ({known_member[1]})
'original_sender': sender, 'original_sender': sender,
'original_subject': subject, 'original_subject': subject,
'original_body': body, 'original_body': body,
'message_id': message_id, # für Journal-Update durch TaskWorker 'message_id': message_id,
'imap_uid': email_id.decode(), 'imap_uid': email_id.decode(),
'extra_context': extra_context, # Für unbekannte Absender 'extra_context': extra_context,
} }
tasks.append(task)
with task_queue_lock: with task_queue_lock:
task_queue.append(task) task_queue.append(task)
@ -1627,7 +1741,7 @@ Diese Email kommt von **{known_member[0]}** ({known_member[1]})
log_entry['status'] = 'queued' log_entry['status'] = 'queued'
add_to_email_log(log_entry) add_to_email_log(log_entry)
logger.info("[EmailPoller] Task #%d für Email von %s in Queue eingereiht.", task['id'], sender_email) logger.info("[EmailPoller] Task #%d für Email von %s in Queue eingereiht.", task_id, sender_email)
except Exception as e: except Exception as e:
logger.error("[EmailPoller] Fehler bei Email-ID %s: %s", email_id, str(e)) logger.error("[EmailPoller] Fehler bei Email-ID %s: %s", email_id, str(e))
@ -1817,10 +1931,9 @@ Agent-Beschreibungen:
assigned = available_agents[0] assigned = available_agents[0]
# Sub-Task mit KOMPLETTEM Kontext # Sub-Task mit KOMPLETTEM Kontext
sub_task = { sub_task_id = create_task(
'id': len(tasks) + 1 + len(created_sub_tasks), title=t[:80],
'title': t[:80], description=f"""**Original-Aufgabe:**
'description': f"""**Original-Aufgabe:**
{task.get('title', '')} {task.get('title', '')}
{task.get('description', '')} {task.get('description', '')}
@ -1832,16 +1945,13 @@ Agent-Beschreibungen:
{t} {t}
Arbeite diesen Teil ab und liefere ein vollständiges Ergebnis.""", Arbeite diesen Teil ab und liefere ein vollständiges Ergebnis.""",
'assigned_agent': AGENTS.get(assigned, {}).get('name', assigned), agent_key=assigned,
'agent_key': assigned, task_type='orchestrated',
'status': 'pending', created_by='orchestrator',
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), parent_task_id=task['id']
'type': 'orchestrated', )
'parent_task': task['id'] created_sub_tasks.append(sub_task_id)
} logger.info("[TaskBeat] Sub-Task #%d zugewiesen an %s", sub_task_id, assigned)
tasks.append(sub_task)
created_sub_tasks.append(sub_task['id'])
logger.info("[TaskBeat] Sub-Task #%d zugewiesen an %s", sub_task['id'], assigned)
task['status'] = 'completed' task['status'] = 'completed'
task['sub_task_ids'] = created_sub_tasks task['sub_task_ids'] = created_sub_tasks
@ -1982,12 +2092,12 @@ Nutze @READ_KNOWLEDGE um Kontext zu holen falls nötig.
logger.info("[OrchestratorBeat] Frage Orchestrator zu %d Problemen", len(old_pending) + len(stuck_tasks)) logger.info("[OrchestratorBeat] Frage Orchestrator zu %d Problemen", len(old_pending) + len(stuck_tasks))
response = execute_agent_task('orchestrator', summary) response = execute_agent_task('orchestrator', summary)
# Response loggen # Response loggen und Kommandos parsen
if response: if response:
logger.info("[OrchestratorBeat] Orchestrator-Antwort: %s", response[:200]) logger.info("[OrchestratorBeat] Orchestrator-Antwort: %s", response[:200])
# TODO: Parse @SEND_EMAIL und @SEND_TELEGRAM Kommandos # Parse Orchestrator-Kommandos (Email, Telegram, etc.)
# und sende tatsächlich Benachrichtigungen parse_agent_commands('orchestrator', response)
except Exception as e: except Exception as e:
logger.error("[OrchestratorBeat] Fehler: %s", str(e)) logger.error("[OrchestratorBeat] Fehler: %s", str(e))
@ -2008,7 +2118,9 @@ start_orchestrator_beat()
@app.route('/') @app.route('/')
def index(): def index():
recent_tasks = tasks[-5:] if tasks else [] # Hole die 5 neuesten Tasks aus DB
all_tasks = get_tasks()
recent_tasks = all_tasks[:5] if all_tasks else []
return render_template('index.html', agents=AGENTS, recent_tasks=recent_tasks) return render_template('index.html', agents=AGENTS, recent_tasks=recent_tasks)
@app.route('/chat', methods=['GET', 'POST']) @app.route('/chat', methods=['GET', 'POST'])
@ -2089,28 +2201,30 @@ def task_list():
assigned_agent = request.form.get('assigned_agent', '') assigned_agent = request.form.get('assigned_agent', '')
if title: if title:
task = { task_id = create_task(
'id': len(tasks) + 1, title=title,
'title': title, description=description,
'description': description, agent_key=assigned_agent if assigned_agent else None,
'assigned_agent': AGENTS.get(assigned_agent, {}).get('name', assigned_agent) if assigned_agent else 'Nicht zugewiesen', task_type='manual',
'status': 'pending', created_by='user'
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), )
'type': 'manual', flash(f'Task #{task_id} erstellt!', 'success')
}
tasks.append(task)
flash('Task erstellt!', 'success')
# Alle Tasks anzeigen Email-Tasks sind mit type='email' markiert # Alle Tasks aus Datenbank holen Neueste zuerst
all_tasks = list(reversed(tasks)) # Neueste zuerst all_tasks = get_tasks()
return render_template('tasks.html', agents=AGENTS, tasks=all_tasks) return render_template('tasks.html', agents=AGENTS, tasks=all_tasks)
@app.route('/tasks/update/<int:task_id>/<status>') @app.route('/tasks/update/<int:task_id>/<status>')
def update_task(task_id, status): def update_task(task_id, status):
# Update in Datenbank
update_task_db(task_id, status=status)
# Auch in-memory array aktualisieren (Legacy-Kompatibilität für task_queue)
for task in tasks: for task in tasks:
if task['id'] == task_id: if task['id'] == task_id:
task['status'] = status task['status'] = status
break break
return redirect(url_for('task_list')) return redirect(url_for('task_list'))
@app.route('/api/agent-stream', methods=['POST']) @app.route('/api/agent-stream', methods=['POST'])
@ -2698,26 +2812,35 @@ def api_tasks():
if not title: if not title:
return jsonify({'error': 'Kein Titel übergeben'}), 400 return jsonify({'error': 'Kein Titel übergeben'}), 400
task_id = len(tasks) + 1 task_id = create_task(
while any(t['id'] == task_id for t in tasks): title=title,
task_id += 1 description=description,
agent_key=agent_key or assigned_agent or 'orchestrator',
task_type='agent_created',
created_by=agent_key or 'api'
)
# Task aus DB holen für Response
con = sqlite3.connect(EMAIL_JOURNAL_DB)
task_row = con.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
con.close()
new_task = { new_task = {
'id': task_id, 'id': task_id,
'title': title, 'title': title,
'description': description, 'description': description,
'assigned_agent': AGENTS.get(assigned_agent, {}).get('name', assigned_agent) if assigned_agent else 'Nicht zugewiesen', 'assigned_agent': AGENTS.get(agent_key or assigned_agent, {}).get('name', agent_key or assigned_agent) if (agent_key or assigned_agent) else 'Nicht zugewiesen',
'agent_key': agent_key or assigned_agent, 'agent_key': agent_key or assigned_agent or 'orchestrator',
'status': 'pending', 'status': 'pending',
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), 'created': datetime.now().strftime('%Y-%m-%d %H:%M'),
'type': 'agent_created', 'type': 'agent_created',
'created_by': agent_key 'created_by': agent_key or 'api'
} }
tasks.append(new_task)
return jsonify({'success': True, 'task': new_task}) return jsonify({'success': True, 'task': new_task})
task_list = list(reversed(tasks)) # GET: Alle Tasks aus DB
task_list = get_tasks()
return jsonify({'tasks': task_list}) return jsonify({'tasks': task_list})
@ -2727,12 +2850,17 @@ def update_task_api(task_id):
data = request.get_json() data = request.get_json()
new_status = data.get('status') new_status = data.get('status')
# Update in DB
update_task_db(task_id, status=new_status)
# Auch in-memory aktualisieren (Legacy)
for task in tasks: for task in tasks:
if task['id'] == task_id: if task['id'] == task_id:
task['status'] = new_status task['status'] = new_status
return jsonify({'success': True, 'task': task}) return jsonify({'success': True, 'task': task})
return jsonify({'error': 'Task nicht gefunden'}), 404 # Falls nur in DB vorhanden
return jsonify({'success': True, 'task_id': task_id, 'status': new_status})
@app.route('/api/models', methods=['GET']) @app.route('/api/models', methods=['GET'])
@ -2817,43 +2945,35 @@ def distribute_tasks():
tasks_text = '\n'.join([f"- {t}" for t in tasks_list]) tasks_text = '\n'.join([f"- {t}" for t in tasks_list])
agents_text = ', '.join(selected_agents) if selected_agents else 'alle verfügbaren Agenten' agents_text = ', '.join(selected_agents) if selected_agents else 'alle verfügbaren Agenten'
planning_task = { planning_task_id = create_task(
'id': len(tasks) + 1, title=f"Planungsphase: {tasks_list[0][:50]}{'...' if len(tasks_list[0]) > 50 else ''}",
'title': f"Planungsphase: {tasks_list[0][:50]}{'...' if len(tasks_list[0]) > 50 else ''}", description=f"Tasks:\n{tasks_text}\n\nVerfügbare Agenten: {agents_text}\n\nDer Orchestrator soll diese Tasks analysieren und den richtigen Agenten zuweisen.",
'description': f"Tasks:\n{tasks_text}\n\nVerfügbare Agenten: {agents_text}\n\nDer Orchestrator soll diese Tasks analysieren und den richtigen Agenten zuweisen.", agent_key='orchestrator',
'assigned_agent': 'Orchestrator', task_type='orchestrated',
'agent_key': 'orchestrator', created_by='orchestrator',
'status': 'pending', sub_tasks=tasks_list,
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), available_agents=selected_agents
'type': 'orchestrated', )
'sub_tasks': tasks_list,
'available_agents': selected_agents
}
tasks.append(planning_task)
return jsonify({ return jsonify({
'success': True, 'success': True,
'message': f'Planungs-Task erstellt. Der Orchestrator wird die richtigen Agenten zuweisen.', 'message': f'Planungs-Task erstellt. Der Orchestrator wird die richtigen Agenten zuweisen.',
'tasks': [planning_task['id']] 'tasks': [planning_task_id]
}) })
for i, task_text in enumerate(tasks_list): for i, task_text in enumerate(tasks_list):
agent_key = selected_agents[i % len(selected_agents)] agent_key = selected_agents[i % len(selected_agents)]
agent_info = AGENTS.get(agent_key, {})
agent_name = agent_info.get('name', agent_key)
new_task = { task_id = create_task(
'id': len(tasks) + 1 + i, title=task_text[:80],
'title': task_text[:80], description='Automatisch erstellt durch Orchestrator',
'description': 'Automatisch erstellt durch Orchestrator', agent_key=agent_key,
'assigned_agent': agent_name, task_type='orchestrated',
'status': 'in_progress', created_by='orchestrator'
'created': datetime.now().strftime('%Y-%m-%d %H:%M'), )
'type': 'orchestrated', # Setze Status sofort auf in_progress
'agent_key': agent_key update_task_db(task_id, status='in_progress')
} created_tasks.append(task_id)
tasks.append(new_task)
created_tasks.append(new_task['id'])
executor = concurrent.futures.ThreadPoolExecutor(max_workers=len(selected_agents)) executor = concurrent.futures.ThreadPoolExecutor(max_workers=len(selected_agents))
for i, task_text in enumerate(tasks_list): for i, task_text in enumerate(tasks_list):