fix(brain): Trigger-Turn blockiert nicht mehr den Event-Loop

_fire() rief agent.chat() (synchroner, bis zu 24h blockierender Proxy-
Call) DIREKT im asyncio-Loop auf → ein feuernder Timer/Watcher fror den
GESAMTEN Brain ein (kein /health, kein weiterer Request, Self-Deadlock
wenn der Turn via brain_request auf den Brain zurueckgreift). Jetzt wie
der /chat-Pfad in run_in_executor ausgelagert; Event-Loop bleibt frei.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 02:31:47 +02:00
co-authored by Claude Opus 4.8
parent 2aef0347ae
commit 70705269fd
+13 -4
View File
@@ -149,14 +149,23 @@ async def _fire(trigger: dict, agent_factory) -> None:
) )
try: try:
agent = agent_factory() # WICHTIG: agent.chat() ist ein SYNCHRONER, blockierender Aufruf (Proxy-
reply, _, _, _, _ = agent.chat(prompt, source="trigger") # HTTP mit bis zu 24h Read-Timeout). NIEMALS direkt im async-Loop —
events = agent.pop_events() # sonst friert ein einziger getriggerter Turn den GESAMTEN Brain ein
# (kein /health, kein weiterer Request). Wie der /chat-Pfad in den
# Executor auslagern, damit der Event-Loop frei bleibt.
loop = asyncio.get_running_loop()
def _run_turn():
a = agent_factory()
rep, *_rest = a.chat(prompt, source="trigger")
return rep, a.pop_events()
reply, events = await loop.run_in_executor(None, _run_turn)
logger.info("[trigger] %s gefeuert → ARIA-Reply: %s", name, reply[:80]) logger.info("[trigger] %s gefeuert → ARIA-Reply: %s", name, reply[:80])
triggers_mod.append_log(name, {"event": "reply", "text": reply[:500]}) triggers_mod.append_log(name, {"event": "reply", "text": reply[:500]})
# Reply an die Bridge pushen, damit App + Diagnostic + TTS sie kriegen. # Reply an die Bridge pushen, damit App + Diagnostic + TTS sie kriegen.
# Ohne diesen Push wuerde die Antwort nur im Brain-Log landen. # Ohne diesen Push wuerde die Antwort nur im Brain-Log landen.
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, _push_to_bridge, reply, name, ttype, events) await loop.run_in_executor(None, _push_to_bridge, reply, name, ttype, events)
except Exception as e: except Exception as e:
logger.exception("Trigger %s feuern fehlgeschlagen: %s", name, e) logger.exception("Trigger %s feuern fehlgeschlagen: %s", name, e)