diff --git a/fern/openapi.json b/fern/openapi.json index 54e8a564..0f50834d 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -30372,6 +30372,9 @@ }, { "$ref": "#/components/schemas/ApprovalCreate" + }, + { + "$ref": "#/components/schemas/ToolReturnCreate" } ] }, @@ -36732,6 +36735,9 @@ }, { "$ref": "#/components/schemas/ApprovalCreate" + }, + { + "$ref": "#/components/schemas/ToolReturnCreate" } ] }, @@ -36924,6 +36930,9 @@ }, { "$ref": "#/components/schemas/ApprovalCreate" + }, + { + "$ref": "#/components/schemas/ToolReturnCreate" } ] }, @@ -37296,6 +37305,9 @@ }, { "$ref": "#/components/schemas/ApprovalCreate" + }, + { + "$ref": "#/components/schemas/ToolReturnCreate" } ] }, @@ -37543,6 +37555,9 @@ }, { "$ref": "#/components/schemas/ApprovalCreate" + }, + { + "$ref": "#/components/schemas/ToolReturnCreate" } ] }, @@ -44706,6 +44721,29 @@ "required": ["tool_call_id", "content", "is_error"], "title": "ToolReturnContent" }, + "ToolReturnCreate": { + "properties": { + "type": { + "type": "string", + "const": "tool_return", + "title": "Type", + "description": "The message type to be created.", + "default": "tool_return" + }, + "tool_returns": { + "items": { + "$ref": "#/components/schemas/letta__schemas__letta_message__ToolReturn" + }, + "type": "array", + "title": "Tool Returns", + "description": "List of tool returns from client-side execution" + } + }, + "type": "object", + "required": ["tool_returns"], + "title": "ToolReturnCreate", + "description": "Submit tool return(s) from client-side tool execution.\n\nThis is the preferred way to send tool results back to the agent after\nclient-side tool execution. It is equivalent to sending an ApprovalCreate\nwith tool return approvals, but provides a cleaner API for the common case." + }, "ToolReturnMessage": { "properties": { "id": { diff --git a/letta/agents/helpers.py b/letta/agents/helpers.py index 2ce15c0f..41328da5 100644 --- a/letta/agents/helpers.py +++ b/letta/agents/helpers.py @@ -15,7 +15,7 @@ from letta.schemas.letta_message import MessageType from letta.schemas.letta_message_content import TextContent from letta.schemas.letta_response import LettaResponse from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.message import ApprovalCreate, Message, MessageCreate, MessageCreateBase +from letta.schemas.message import ApprovalCreate, Message, MessageCreate, MessageCreateBase, ToolReturnCreate from letta.schemas.tool_execution_result import ToolExecutionResult from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User @@ -242,6 +242,14 @@ 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) + # Convert ToolReturnCreate to ApprovalCreate for unified processing + if input_messages[0].type == "tool_return": + tool_return_msg = input_messages[0] + input_messages = [ + ApprovalCreate(approvals=tool_return_msg.tool_returns), + *input_messages[1:], + ] + # Check for approval-related message validation if input_messages[0].type == "approval": # User is trying to send an approval response diff --git a/letta/schemas/message.py b/letta/schemas/message.py index 9274044a..99902d7d 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -128,6 +128,7 @@ def add_inner_thoughts_to_tool_call( class MessageCreateType(str, Enum): message = "message" approval = "approval" + tool_return = "tool_return" class MessageCreateBase(BaseModel): @@ -188,7 +189,24 @@ class ApprovalCreate(MessageCreateBase): return self -MessageCreateUnion = Union[MessageCreate, ApprovalCreate] +class ToolReturnCreate(MessageCreateBase): + """Submit tool return(s) from client-side tool execution. + + This is the preferred way to send tool results back to the agent after + client-side tool execution. It is equivalent to sending an ApprovalCreate + with tool return approvals, but provides a cleaner API for the common case. + """ + + type: Literal[MessageCreateType.tool_return] = Field( + default=MessageCreateType.tool_return, description="The message type to be created." + ) + tool_returns: List[LettaToolReturn] = Field( + ..., + description="List of tool returns from client-side execution", + ) + + +MessageCreateUnion = Union[MessageCreate, ApprovalCreate, ToolReturnCreate] class MessageUpdate(BaseModel):