fix: new versions of send_message_to_agent that are async (#725)
Co-authored-by: Matt Zhou <mattzh1314@gmail.com>
This commit is contained in:
@@ -1,80 +1,86 @@
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from letta.constants import MULTI_AGENT_SEND_MESSAGE_MAX_RETRIES, MULTI_AGENT_SEND_MESSAGE_TIMEOUT
|
||||
from letta.functions.helpers import async_send_message_with_retries
|
||||
from letta.orm.errors import NoResultFound
|
||||
from letta.functions.helpers import async_send_message_with_retries, execute_send_message_to_agent, fire_and_forget_send_to_agent
|
||||
from letta.schemas.enums import MessageRole
|
||||
from letta.schemas.message import MessageCreate
|
||||
from letta.server.rest_api.utils import get_letta_server
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from letta.agent import Agent
|
||||
|
||||
|
||||
def send_message_to_specific_agent(self: "Agent", message: str, other_agent_id: str) -> Optional[str]:
|
||||
def send_message_to_agent_and_wait_for_reply(self: "Agent", message: str, other_agent_id: str) -> str:
|
||||
"""
|
||||
Send a message to a specific Letta agent within the same organization.
|
||||
Sends a message to a specific Letta agent within the same organization and waits for a response. The sender's identity is automatically included, so no explicit introduction is needed in the message. This function is designed for two-way communication where a reply is expected.
|
||||
|
||||
Args:
|
||||
message (str): The message to be sent to the target Letta agent.
|
||||
other_agent_id (str): The identifier of the target Letta agent.
|
||||
message (str): The content of the message to be sent to the target agent.
|
||||
other_agent_id (str): The unique identifier of the target Letta agent.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The response from the Letta agent. It's possible that the agent does not respond.
|
||||
str: The response from the target agent.
|
||||
"""
|
||||
server = get_letta_server()
|
||||
messages = [MessageCreate(role=MessageRole.user, content=message, name=self.agent_state.name)]
|
||||
return execute_send_message_to_agent(
|
||||
sender_agent=self,
|
||||
messages=messages,
|
||||
other_agent_id=other_agent_id,
|
||||
log_prefix="[send_message_to_agent_and_wait_for_reply]",
|
||||
)
|
||||
|
||||
# Ensure the target agent is in the same org
|
||||
try:
|
||||
server.agent_manager.get_agent_by_id(agent_id=other_agent_id, actor=self.user)
|
||||
except NoResultFound:
|
||||
raise ValueError(
|
||||
f"The passed-in agent_id {other_agent_id} either does not exist, "
|
||||
f"or does not belong to the same org ({self.user.organization_id})."
|
||||
)
|
||||
|
||||
# Async logic to send a message with retries and timeout
|
||||
async def async_send_single_agent():
|
||||
return await async_send_message_with_retries(
|
||||
server=server,
|
||||
sender_agent=self,
|
||||
target_agent_id=other_agent_id,
|
||||
message_text=message,
|
||||
max_retries=MULTI_AGENT_SEND_MESSAGE_MAX_RETRIES, # or your chosen constants
|
||||
timeout=MULTI_AGENT_SEND_MESSAGE_TIMEOUT, # e.g., 1200 for 20 min
|
||||
logging_prefix="[send_message_to_specific_agent]",
|
||||
)
|
||||
def send_message_to_agent_async(self: "Agent", message: str, other_agent_id: str) -> str:
|
||||
"""
|
||||
Sends a message to a specific Letta agent within the same organization. The sender's identity is automatically included, so no explicit introduction is required in the message. This function does not expect a response from the target agent, making it suitable for notifications or one-way communication.
|
||||
|
||||
# Run in the current event loop or create one if needed
|
||||
try:
|
||||
return asyncio.run(async_send_single_agent())
|
||||
except RuntimeError:
|
||||
# e.g., in case there's already an active loop
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
return loop.run_until_complete(async_send_single_agent())
|
||||
else:
|
||||
raise
|
||||
Args:
|
||||
message (str): The content of the message to be sent to the target agent.
|
||||
other_agent_id (str): The unique identifier of the target Letta agent.
|
||||
|
||||
Returns:
|
||||
str: A confirmation message indicating the message was successfully sent.
|
||||
"""
|
||||
message = (
|
||||
f"[Incoming message from agent with ID '{self.agent_state.id}' - to reply to this message, "
|
||||
f"make sure to use the 'send_message_to_agent_async' tool, or the agent will not receive your message] "
|
||||
f"{message}"
|
||||
)
|
||||
messages = [MessageCreate(role=MessageRole.system, content=message, name=self.agent_state.name)]
|
||||
|
||||
# Do the actual fire-and-forget
|
||||
fire_and_forget_send_to_agent(
|
||||
sender_agent=self,
|
||||
messages=messages,
|
||||
other_agent_id=other_agent_id,
|
||||
log_prefix="[send_message_to_agent_async]",
|
||||
use_retries=False, # or True if you want to use async_send_message_with_retries
|
||||
)
|
||||
|
||||
# Immediately return to caller
|
||||
return "Successfully sent message"
|
||||
|
||||
|
||||
def send_message_to_agents_matching_all_tags(self: "Agent", message: str, tags: List[str]) -> List[str]:
|
||||
"""
|
||||
Send a message to all agents in the same organization that match ALL of the given tags.
|
||||
|
||||
Messages are sent in parallel for improved performance, with retries on flaky calls and timeouts for long-running requests.
|
||||
This function does not use a cursor (pagination) and enforces a limit of 100 agents.
|
||||
Sends a message to all agents within the same organization that match all of the specified tags. Messages are dispatched in parallel for improved performance, with retries to handle transient issues and timeouts to ensure responsiveness. This function enforces a limit of 100 agents and does not support pagination (cursor-based queries). Each agent must match all specified tags (`match_all_tags=True`) to be included.
|
||||
|
||||
Args:
|
||||
message (str): The message to be sent to each matching agent.
|
||||
tags (List[str]): The list of tags that each agent must have (match_all_tags=True).
|
||||
message (str): The content of the message to be sent to each matching agent.
|
||||
tags (List[str]): A list of tags that an agent must possess to receive the message.
|
||||
|
||||
Returns:
|
||||
List[str]: A list of responses from the agents that match all tags.
|
||||
Each response corresponds to one agent.
|
||||
List[str]: A list of responses from the agents that matched all tags. Each
|
||||
response corresponds to a single agent. Agents that do not respond will not
|
||||
have an entry in the returned list.
|
||||
"""
|
||||
|
||||
server = get_letta_server()
|
||||
|
||||
# Retrieve agents that match ALL specified tags
|
||||
matching_agents = server.agent_manager.list_agents(actor=self.user, tags=tags, match_all_tags=True, limit=100)
|
||||
messages = [MessageCreate(role=MessageRole.user, content=message, name=self.agent_state.name)]
|
||||
|
||||
async def send_messages_to_all_agents():
|
||||
tasks = [
|
||||
@@ -82,7 +88,7 @@ def send_message_to_agents_matching_all_tags(self: "Agent", message: str, tags:
|
||||
server=server,
|
||||
sender_agent=self,
|
||||
target_agent_id=agent_state.id,
|
||||
message_text=message,
|
||||
messages=messages,
|
||||
max_retries=MULTI_AGENT_SEND_MESSAGE_MAX_RETRIES,
|
||||
timeout=MULTI_AGENT_SEND_MESSAGE_TIMEOUT,
|
||||
logging_prefix="[send_message_to_agents_matching_all_tags]",
|
||||
|
||||
Reference in New Issue
Block a user