From 2f8d2e10daa86547bec8b68279ce7f6fa975e29a Mon Sep 17 00:00:00 2001 From: majdyz Date: Tue, 21 Apr 2026 23:06:24 +0700 Subject: [PATCH] fix(backend/copilot): clear inflight tool-call buffer at top of outer finally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review on #12871 flagged that `session.clear_inflight_tool_calls()` ran after usage persistence, session upsert and transcript upload in the baseline turn `finally`, so if any of those awaited cleanup steps raised, the process-local scratch buffer would leak into the next turn — the guide-read guard would observe a phantom in-flight call and skip its gate. Move the clear to the very first statement of the outer `finally` so it runs unconditionally once tool execution has ended, before any failure-prone cleanup. Keep the documentation pointing at the observed failure mode. --- .../backend/backend/copilot/baseline/service.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/autogpt_platform/backend/backend/copilot/baseline/service.py b/autogpt_platform/backend/backend/copilot/baseline/service.py index c9724261e9..6aa88e9d41 100644 --- a/autogpt_platform/backend/backend/copilot/baseline/service.py +++ b/autogpt_platform/backend/backend/copilot/baseline/service.py @@ -1822,6 +1822,16 @@ async def stream_chat_completion_baseline( yield StreamError(errorText=error_msg, code="baseline_error") # Still persist whatever we got finally: + # In-flight tool-call announcements are only meaningful for the + # current turn; clear at the top of the outer finally so the next + # turn starts with a clean scratch buffer even if one of the + # awaited cleanup steps below (usage persistence, session upsert, + # transcript upload) raises. The buffer is a process-local scratch + # set — if we leak it into the next turn the guide-read guard would + # observe a phantom in-flight call and skip its gate, so this must + # run unconditionally. + session.clear_inflight_tool_calls() + # Pending messages are drained atomically at turn start and # between tool rounds, so there's nothing to clear in finally. # Any message pushed after the final drain window stays in the @@ -1916,10 +1926,6 @@ async def stream_chat_completion_baseline( final_text = final_text[len(recorded) :] if final_text.strip(): session.messages.append(ChatMessage(role="assistant", content=final_text)) - # In-flight tool-call announcements are only meaningful for the - # current turn; clear before persist so the next turn starts with - # a clean scratch buffer. - session.clear_inflight_tool_calls() try: await upsert_chat_session(session) except Exception as persist_err: