fix: cancellation causing desync for parallel tool calling [LET-7028] (#8739)

This commit is contained in:
cthomas
2026-01-14 22:40:15 -08:00
committed by Sarah Wooders
parent 487bb42231
commit ca43ee0b7b

View File

@@ -665,8 +665,26 @@ class RunManager:
logger.debug(f"Agent was waiting for approval, adding denial messages for run {run_id}")
approval_request_message = current_in_context_messages[-1]
# Ensure the approval request has tool calls to deny
# Find ALL pending tool calls (both requiring approval and not requiring approval)
# The assistant message may have tool calls that didn't require approval
all_pending_tool_calls = []
if approval_request_message.tool_calls:
all_pending_tool_calls.extend(approval_request_message.tool_calls)
# Check if there's an assistant message before the approval request with additional tool calls
if len(current_in_context_messages) >= 2:
potential_assistant_msg = current_in_context_messages[-2]
if potential_assistant_msg.role == MessageRole.assistant and potential_assistant_msg.tool_calls:
# Add any tool calls from the assistant message that aren't already in the approval request
approval_tool_call_ids = (
{tc.id for tc in approval_request_message.tool_calls} if approval_request_message.tool_calls else set()
)
for tool_call in potential_assistant_msg.tool_calls:
if tool_call.id not in approval_tool_call_ids:
all_pending_tool_calls.append(tool_call)
# Ensure we have tool calls to deny
if all_pending_tool_calls:
from letta.constants import TOOL_CALL_DENIAL_ON_CANCEL
from letta.schemas.letta_message import ApprovalReturn
from letta.schemas.message import ApprovalCreate
@@ -676,15 +694,19 @@ class RunManager:
create_tool_returns_for_denials,
)
# Create denials for ALL pending tool calls
denials = [
ApprovalReturn(
tool_call_id=tool_call.id,
approve=False,
reason=TOOL_CALL_DENIAL_ON_CANCEL,
)
for tool_call in approval_request_message.tool_calls
]
# Create denials for ALL pending tool calls (including those that didn't require approval)
denials = (
[
ApprovalReturn(
tool_call_id=tool_call.id,
approve=False,
reason=TOOL_CALL_DENIAL_ON_CANCEL,
)
for tool_call in approval_request_message.tool_calls
]
if approval_request_message.tool_calls
else []
)
# Create an ApprovalCreate input with the denials
approval_input = ApprovalCreate(
@@ -700,9 +722,9 @@ class RunManager:
)
# Create tool returns for ALL denied tool calls using shared helper
# This handles all pending tool calls at once since they all have the same denial reason
# This includes both tool calls requiring approval AND those that didn't
tool_returns = create_tool_returns_for_denials(
tool_calls=approval_request_message.tool_calls, # ALL pending tool calls
tool_calls=all_pending_tool_calls,
denial_reason=TOOL_CALL_DENIAL_ON_CANCEL,
timezone=agent_state.timezone,
)