Fix task ordering (FIFO) + Telegram standup reply matching
- get_tasks(): ORDER BY id ASC (oldest first) for TaskBeat FIFO processing - UI calls use order='desc' so display stays newest-first - trigger_daily_standup(): standup sub-tasks now stored as type='standup_reply' with telegram_chat_id set correctly so replies can be matched - telegram_message_handler: checks for open standup_reply task from same chat_id before creating new task - if found, marks standup complete and creates orchestrator follow-up task with full context to process the reply - tasks.html: badge for standup_reply type
This commit is contained in:
parent
01858b5fc6
commit
8780c2b176
2 changed files with 89 additions and 22 deletions
109
app.py
109
app.py
|
|
@ -712,16 +712,21 @@ chat_history = []
|
||||||
orchestrator_chat = []
|
orchestrator_chat = []
|
||||||
|
|
||||||
# ── Task Database Functions ─────────────────────────────────────────────────
|
# ── Task Database Functions ─────────────────────────────────────────────────
|
||||||
def get_tasks(status=None, limit=None):
|
def get_tasks(status=None, limit=None, order='asc'):
|
||||||
"""Lädt Tasks aus der Datenbank."""
|
"""Lädt Tasks aus der Datenbank.
|
||||||
|
|
||||||
|
order='asc' → älteste zuerst (Standard für TaskBeat: FIFO)
|
||||||
|
order='desc' → neueste zuerst (für UI-Anzeige)
|
||||||
|
"""
|
||||||
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||||
con.row_factory = sqlite3.Row
|
con.row_factory = sqlite3.Row
|
||||||
|
direction = 'ASC' if order == 'asc' else 'DESC'
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
query = "SELECT * FROM tasks WHERE status = ? ORDER BY id DESC"
|
query = f"SELECT * FROM tasks WHERE status = ? ORDER BY id {direction}"
|
||||||
params = (status,)
|
params = (status,)
|
||||||
else:
|
else:
|
||||||
query = "SELECT * FROM tasks ORDER BY id DESC"
|
query = f"SELECT * FROM tasks ORDER BY id {direction}"
|
||||||
params = ()
|
params = ()
|
||||||
|
|
||||||
if limit:
|
if limit:
|
||||||
|
|
@ -1596,30 +1601,84 @@ async def telegram_message_handler(update: Update, context: ContextTypes.DEFAULT
|
||||||
user_id = update.effective_user.id
|
user_id = update.effective_user.id
|
||||||
username = update.effective_user.username or update.effective_user.first_name
|
username = update.effective_user.username or update.effective_user.first_name
|
||||||
message_text = update.message.text
|
message_text = update.message.text
|
||||||
|
chat_id = update.effective_chat.id
|
||||||
|
|
||||||
# Whitelist-Check
|
# Whitelist-Check
|
||||||
if TELEGRAM_CONFIG['allowed_users'] and user_id not in TELEGRAM_CONFIG['allowed_users']:
|
if TELEGRAM_CONFIG['allowed_users'] and user_id not in TELEGRAM_CONFIG['allowed_users']:
|
||||||
await update.message.reply_text("⛔ Zugriff verweigert. Verwende /start für Details.")
|
await update.message.reply_text("⛔ Zugriff verweigert. Verwende /start für Details.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Task erstellen
|
# ── Prüfe ob diese Nachricht eine Antwort auf einen offenen standup_reply-Task ist ──
|
||||||
|
try:
|
||||||
|
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||||
|
con.row_factory = sqlite3.Row
|
||||||
|
open_standup = con.execute(
|
||||||
|
"SELECT id, parent_task_id, title FROM tasks "
|
||||||
|
"WHERE type='standup_reply' AND status='pending' AND telegram_chat_id=? "
|
||||||
|
"ORDER BY id ASC LIMIT 1",
|
||||||
|
(chat_id,)
|
||||||
|
).fetchone()
|
||||||
|
con.close()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[Telegram] DB-Fehler beim Standup-Check: {e}")
|
||||||
|
open_standup = None
|
||||||
|
|
||||||
|
if open_standup:
|
||||||
|
# Antwort auf offenen Standup-Task → mit Kontext an Orchestrator weiterleiten
|
||||||
|
standup_task_id = open_standup['id']
|
||||||
|
parent_id = open_standup['parent_task_id']
|
||||||
|
|
||||||
|
# Orchestrator-Task erstellen: verarbeite die Standup-Antwort
|
||||||
|
orchestrator_prompt = (
|
||||||
|
f"## Standup-Antwort von {username}\n\n"
|
||||||
|
f"**Antwort:** {message_text}\n\n"
|
||||||
|
f"**Aufgabe:**\n"
|
||||||
|
f"1. Verarbeite diese Standup-Antwort und extrahiere wichtige Informationen.\n"
|
||||||
|
f"2. Falls neue Informationen enthalten sind (Änderungen, Entscheidungen, Probleme): "
|
||||||
|
f"aktualisiere die Wissensdatenbank mit `<update_knowledge>` und informiere betroffene Agenten mit `<update_agent_reminder>`.\n"
|
||||||
|
f"3. Falls Handlungsbedarf besteht: erstelle entsprechende Tasks mit `<create_task>`.\n"
|
||||||
|
f"4. Antworte {username} kurz per Telegram (telegram_id: {chat_id}) und bestätige den Empfang."
|
||||||
|
)
|
||||||
|
followup_id = create_task(
|
||||||
|
title=f"Standup-Antwort verarbeiten: {username}",
|
||||||
|
description=orchestrator_prompt,
|
||||||
|
agent_key='orchestrator',
|
||||||
|
task_type='telegram',
|
||||||
|
created_by=f'telegram_user_{user_id}',
|
||||||
|
telegram_chat_id=chat_id,
|
||||||
|
telegram_user=username,
|
||||||
|
parent_task_id=parent_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Standup-reply-Task als beantwortet markieren
|
||||||
|
update_task_db(standup_task_id, status='completed',
|
||||||
|
response=f"Antwort erhalten von {username}: {message_text[:200]}")
|
||||||
|
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"✅ Danke {username}! Deine Standup-Antwort wurde aufgenommen.\n\n"
|
||||||
|
f"📝 Der Orchestrator verarbeitet dein Update (Task #{followup_id})."
|
||||||
|
)
|
||||||
|
logging.info(f"[Telegram] Standup-Antwort von {username} → Task #{standup_task_id} completed, Follow-up #{followup_id} erstellt")
|
||||||
|
return
|
||||||
|
|
||||||
|
# ── Normale Nachricht → neuer Orchestrator-Task ──────────────────────────
|
||||||
task_id = create_task(
|
task_id = create_task(
|
||||||
title=f"Telegram: {message_text[:50]}{'...' if len(message_text) > 50 else ''}",
|
title=f"Telegram: {message_text[:50]}{'...' if len(message_text) > 50 else ''}",
|
||||||
description=message_text,
|
description=message_text,
|
||||||
agent_key='orchestrator',
|
agent_key='orchestrator',
|
||||||
task_type='telegram',
|
task_type='telegram',
|
||||||
created_by=f'telegram_user_{user_id}',
|
created_by=f'telegram_user_{user_id}',
|
||||||
telegram_chat_id=update.effective_chat.id,
|
telegram_chat_id=chat_id,
|
||||||
telegram_user=username
|
telegram_user=username
|
||||||
)
|
)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
f"✅ Task #{task_id} erstellt!\n\n"
|
f"✅ Task #{task_id} erstellt!\n\n"
|
||||||
f"📝 {message_text[:100]}{'...' if len(message_text) > 100 else ''}\n\n"
|
f"📝 {message_text[:100]}{'...' if len(message_text) > 100 else ''}\n\n"
|
||||||
f"⏳ Der Orchestrator wird deine Anfrage verarbeiten.\n"
|
f"⏳ Der Orchestrator wird deine Anfrage verarbeiten.\n"
|
||||||
f"Ich benachrichtige dich, sobald die Antwort bereit ist."
|
f"Ich benachrichtige dich, sobald die Antwort bereit ist."
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info(f"[Telegram] Task #{task_id} created by {username} (ID: {user_id})")
|
logging.info(f"[Telegram] Task #{task_id} created by {username} (ID: {user_id})")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2399,23 +2458,29 @@ def trigger_daily_standup():
|
||||||
|
|
||||||
# Telegram bevorzugen, Fallback auf Email
|
# Telegram bevorzugen, Fallback auf Email
|
||||||
if telegram_id:
|
if telegram_id:
|
||||||
create_task(
|
standup_sub_id = create_task(
|
||||||
title=f"Standup-Frage an {name} (Telegram)",
|
title=f"Standup-Antwort ausstehend: {name}",
|
||||||
description=f"Telegram-ID: {telegram_id}\nNachricht: {msg}",
|
description=(
|
||||||
|
f"Standup-Frage wurde per Telegram an {name} gesendet.\n"
|
||||||
|
f"Warte auf Antwort...\n\n"
|
||||||
|
f"Gesendete Frage:\n{msg}"
|
||||||
|
),
|
||||||
agent_key='orchestrator',
|
agent_key='orchestrator',
|
||||||
task_type='action_telegram',
|
task_type='standup_reply',
|
||||||
created_by='system',
|
created_by='system',
|
||||||
parent_task_id=standup_task_id,
|
parent_task_id=standup_task_id,
|
||||||
|
telegram_chat_id=int(telegram_id),
|
||||||
|
telegram_user=name,
|
||||||
)
|
)
|
||||||
send_telegram_message(telegram_id, msg)
|
send_telegram_message(telegram_id, msg)
|
||||||
logger.info("[DailyStandup] Telegram-Standup gesendet an %s (%s)", name, telegram_id)
|
logger.info("[DailyStandup] Telegram-Standup gesendet an %s (%s), Task #%s wartet auf Antwort", name, telegram_id, standup_sub_id)
|
||||||
elif email:
|
elif email:
|
||||||
subject = f"Daily Standup {today} — Was gibt's Neues?"
|
subject = f"Daily Standup {today} — Was gibt's Neues?"
|
||||||
create_task(
|
create_task(
|
||||||
title=f"Standup-Frage an {name} (Email)",
|
title=f"Standup-Antwort ausstehend: {name} (Email)",
|
||||||
description=f"An: {email}\nBetreff: {subject}\n\n{msg}",
|
description=f"An: {email}\nBetreff: {subject}\n\n{msg}",
|
||||||
agent_key='orchestrator',
|
agent_key='orchestrator',
|
||||||
task_type='action_email',
|
task_type='standup_reply',
|
||||||
created_by='system',
|
created_by='system',
|
||||||
parent_task_id=standup_task_id,
|
parent_task_id=standup_task_id,
|
||||||
)
|
)
|
||||||
|
|
@ -2580,7 +2645,7 @@ def logout():
|
||||||
@login_required
|
@login_required
|
||||||
def index():
|
def index():
|
||||||
# Hole die 5 neuesten Tasks aus DB
|
# Hole die 5 neuesten Tasks aus DB
|
||||||
all_tasks = get_tasks()
|
all_tasks = get_tasks(order='desc')
|
||||||
recent_tasks = all_tasks[:5] if all_tasks else []
|
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)
|
||||||
|
|
||||||
|
|
@ -2713,8 +2778,8 @@ def task_list():
|
||||||
)
|
)
|
||||||
flash(f'Task #{task_id} erstellt!', 'success')
|
flash(f'Task #{task_id} erstellt!', 'success')
|
||||||
|
|
||||||
# Alle Tasks aus Datenbank holen – Neueste zuerst
|
# Alle Tasks aus Datenbank holen – Neueste zuerst (für UI)
|
||||||
all_tasks = get_tasks()
|
all_tasks = get_tasks(order='desc')
|
||||||
return render_template('tasks.html', agents=AGENTS, tasks=all_tasks)
|
return render_template('tasks.html', agents=AGENTS, tasks=all_tasks)
|
||||||
|
|
||||||
@app.route('/tasks/<int:task_id>')
|
@app.route('/tasks/<int:task_id>')
|
||||||
|
|
@ -3577,8 +3642,8 @@ def api_tasks():
|
||||||
|
|
||||||
return jsonify({'success': True, 'task': new_task})
|
return jsonify({'success': True, 'task': new_task})
|
||||||
|
|
||||||
# GET: Alle Tasks aus DB
|
# GET: Alle Tasks aus DB (neueste zuerst für API)
|
||||||
task_list = get_tasks()
|
task_list = get_tasks(order='desc')
|
||||||
return jsonify({'tasks': task_list})
|
return jsonify({'tasks': task_list})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@
|
||||||
<span class="badge bg-warning ms-1">❓ Frage</span>
|
<span class="badge bg-warning ms-1">❓ Frage</span>
|
||||||
{% elif task.type == 'standup' %}
|
{% elif task.type == 'standup' %}
|
||||||
<span class="badge ms-1" style="background-color:#0d9488;">☀ Standup</span>
|
<span class="badge ms-1" style="background-color:#0d9488;">☀ Standup</span>
|
||||||
|
{% elif task.type == 'standup_reply' %}
|
||||||
|
<span class="badge ms-1" style="background-color:#0d9488;">💬 Standup-Antwort</span>
|
||||||
{% elif task.type == 'broadcast' %}
|
{% elif task.type == 'broadcast' %}
|
||||||
<span class="badge ms-1" style="background-color:#b45309;">📡 Broadcast</span>
|
<span class="badge ms-1" style="background-color:#b45309;">📡 Broadcast</span>
|
||||||
{% elif task.type == 'action_knowledge' %}
|
{% elif task.type == 'action_knowledge' %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue