From 484d2a4e93fb745f8317162cc9c05f69074525df Mon Sep 17 00:00:00 2001 From: cthomas Date: Fri, 29 Aug 2025 20:39:28 -0700 Subject: [PATCH] feat: add error handling for approval response attempt (#4326) * feat: add error handling for approval response attempt * add one more error case * improve error messages --- letta/agents/helpers.py | 21 ++++++++++++++ tests/integration_test_human_in_the_loop.py | 31 +++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/letta/agents/helpers.py b/letta/agents/helpers.py index 43dd3184..b4491bd4 100644 --- a/letta/agents/helpers.py +++ b/letta/agents/helpers.py @@ -148,6 +148,27 @@ async def _prepare_in_context_messages_no_persist_async( # Otherwise, include the full list of messages by ID for context current_in_context_messages = await message_manager.get_messages_by_ids_async(message_ids=agent_state.message_ids, actor=actor) + # Check for approval-related message validation + if len(input_messages) == 1 and input_messages[0].type == "approval": + # User is trying to send an approval response + if current_in_context_messages[-1].role != "approval": + raise ValueError( + "Cannot process approval response: No tool call is currently awaiting approval. " + "Please send a regular message to interact with the agent." + ) + if input_messages[0].approval_request_id != current_in_context_messages[-1].id: + raise ValueError( + f"Invalid approval request ID. Expected '{current_in_context_messages[-1].id}' " + f"but received '{input_messages[0].approval_request_id}'." + ) + else: + # User is trying to send a regular message + if current_in_context_messages[-1].role == "approval": + raise ValueError( + "Cannot send a new message: The agent is waiting for approval on a tool call. " + "Please approve or deny the pending request before continuing." + ) + # Create a new user message from the input but dont store it yet new_in_context_messages = create_input_messages( input_messages=input_messages, agent_id=agent_state.id, timezone=agent_state.timezone, actor=actor diff --git a/tests/integration_test_human_in_the_loop.py b/tests/integration_test_human_in_the_loop.py index a46e574b..bdbe710f 100644 --- a/tests/integration_test_human_in_the_loop.py +++ b/tests/integration_test_human_in_the_loop.py @@ -7,7 +7,8 @@ from typing import Any, List import pytest import requests from dotenv import load_dotenv -from letta_client import Letta, MessageCreate +from letta_client import ApprovalCreate, Letta, MessageCreate +from letta_client.core.api_error import ApiError from letta.log import get_logger from letta.schemas.agent import AgentState @@ -98,7 +99,7 @@ def approval_tool_fixture(client: Letta): client.tools.upsert_base_tools() approval_tool = client.tools.upsert_from_function( func=requires_approval_tool, - # default_requires_approval=True, + # default_requires_approval=True, switch to this once it is supported in sdk ) yield approval_tool @@ -118,6 +119,11 @@ def agent(client: Letta, approval_tool_fixture) -> AgentState: embedding="openai/text-embedding-3-small", tags=["approval_test"], ) + client.agents.tools.modify_approval( + agent_id=agent_state.id, + tool_name=approval_tool_fixture.name, + requires_approval=True, + ) yield agent_state @@ -136,6 +142,13 @@ def test_send_message_with_approval_tool( This test just verifies that the agent can send a message successfully. The actual approval logic testing will be filled out by the user. """ + # Attempt to send approval without pending request + with pytest.raises(ApiError, match="No tool call is currently awaiting approval"): + client.agents.messages.create( + agent_id=agent.id, + messages=[ApprovalCreate(approve=True, approval_request_id="fake_id")], + ) + # Send a simple greeting message to test basic functionality response = client.agents.messages.create( agent_id=agent.id, @@ -147,3 +160,17 @@ def test_send_message_with_approval_tool( assert len(response.messages) == 2 assert response.messages[0].message_type == "reasoning_message" assert response.messages[1].message_type == "approval_request_message" + + # Attempt to send user message - should fail + with pytest.raises(ApiError, match="Please approve or deny the pending request before continuing"): + client.agents.messages.create( + agent_id=agent.id, + messages=[MessageCreate(role="user", content="hi")], + ) + + # Attempt to send approval with incorrect id + with pytest.raises(ApiError, match="Invalid approval request ID"): + client.agents.messages.create( + agent_id=agent.id, + messages=[ApprovalCreate(approve=True, approval_request_id="fake_id")], + )