From 70705269fd7e26b02a70b8fc2f786f2b1d0e64a6 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Tue, 21 Jul 2026 02:31:47 +0200 Subject: [PATCH] fix(brain): Trigger-Turn blockiert nicht mehr den Event-Loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _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 --- aria-brain/background.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/aria-brain/background.py b/aria-brain/background.py index a7d45e8..395c632 100644 --- a/aria-brain/background.py +++ b/aria-brain/background.py @@ -149,14 +149,23 @@ async def _fire(trigger: dict, agent_factory) -> None: ) try: - agent = agent_factory() - reply, _, _, _, _ = agent.chat(prompt, source="trigger") - events = agent.pop_events() + # WICHTIG: agent.chat() ist ein SYNCHRONER, blockierender Aufruf (Proxy- + # HTTP mit bis zu 24h Read-Timeout). NIEMALS direkt im async-Loop — + # 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]) triggers_mod.append_log(name, {"event": "reply", "text": reply[:500]}) # Reply an die Bridge pushen, damit App + Diagnostic + TTS sie kriegen. # 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) except Exception as e: logger.exception("Trigger %s feuern fehlgeschlagen: %s", name, e)