From b1e979f3be4445d40817055d0ef5b01b00d30f3f Mon Sep 17 00:00:00 2001 From: angriff36 Date: Wed, 20 May 2026 17:20:50 -0700 Subject: [PATCH] fix(gateway): offload evaluate_after_turn to thread executor evaluate_after_turn() calls judge_goal() which makes a synchronous HTTP request to the auxiliary LLM. Running it on the event-loop thread blocks Discord heartbeats for 10-40s, causing connection flaps and gateway instability. Offload to the default thread-pool executor so the event loop stays responsive during evaluation. --- gateway/run.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index 16127aae24..a38a81bd98 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -19424,10 +19424,21 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew except Exception: _bg_procs = None - decision = mgr.evaluate_after_turn( - final_response or "", - user_initiated=True, - background_processes=_bg_procs, + # evaluate_after_turn calls judge_goal() which makes a synchronous + # HTTP request to the auxiliary LLM. Running it on the event-loop + # thread would block Discord heartbeats for 10-40 s and cause + # connection flaps, so we offload it to a thread-pool executor. + # _run_in_executor_with_context (not bare run_in_executor): the + # profile secret scope and auxiliary runtime context are contextvars, + # and a default-executor hop would drop them — aux-client provider + # resolution would then read credentials unscoped and fail under + # multiplexing (same pattern as compression in slash_commands.py). + decision = await self._run_in_executor_with_context( + lambda: mgr.evaluate_after_turn( + final_response or "", + user_initiated=True, + background_processes=_bg_procs, + ), ) msg = decision.get("message") or ""