feat: background multi-agent group for sleeptime agent (#1508)

This commit is contained in:
cthomas
2025-04-01 15:00:45 -07:00
committed by GitHub
parent e00b8b5232
commit 55d0ea8872
16 changed files with 663 additions and 52 deletions

View File

@@ -1,12 +1,28 @@
import time
import pytest
from sqlalchemy import delete
from letta.config import LettaConfig
from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS
from letta.functions.functions import parse_source_code
from letta.functions.schema_generator import generate_schema
from letta.orm import Provider, Step
from letta.schemas.agent import CreateAgent
from letta.schemas.block import CreateBlock
from letta.schemas.group import DynamicManager, GroupCreate, GroupUpdate, ManagerType, RoundRobinManager, SupervisorManager
from letta.schemas.block import Block, CreateBlock
from letta.schemas.enums import JobStatus
from letta.schemas.group import (
BackgroundManager,
DynamicManager,
GroupCreate,
GroupUpdate,
ManagerType,
RoundRobinManager,
SupervisorManager,
)
from letta.schemas.message import MessageCreate
from letta.schemas.tool import Tool
from letta.schemas.tool_rule import TerminalToolRule
from letta.server.server import SyncServer
@@ -426,3 +442,129 @@ async def test_dynamic_group_chat(server, actor, manager_agent, participant_agen
finally:
server.group_manager.delete_group(group_id=group.id, actor=actor)
@pytest.mark.asyncio
async def test_background_group_chat(server, actor):
# 1. create shared block
shared_memory_block = server.block_manager.create_or_update_block(
Block(
label="human",
value="",
limit=1000,
),
actor=actor,
)
# 2. create main agent
main_agent = server.create_agent(
request=CreateAgent(
name="main_agent",
memory_blocks=[
CreateBlock(
label="persona",
value="You are a personal assistant that helps users with requests.",
),
],
model="openai/gpt-4o-mini",
embedding="openai/text-embedding-ada-002",
include_base_tools=False,
tools=BASE_TOOLS,
),
actor=actor,
)
# 3. create background memory agent
def skip_memory_update():
"""
Perform no memory updates. This function is used when the transcript
does not require any changes to the memory.
"""
skip_memory_update = Tool(
name=skip_memory_update.__name__,
description="",
source_type="python",
tags=[],
source_code=parse_source_code(skip_memory_update),
json_schema=generate_schema(skip_memory_update, None),
)
skip_memory_update = server.tool_manager.create_or_update_tool(
pydantic_tool=skip_memory_update,
actor=actor,
)
background_memory_agent = server.create_agent(
request=CreateAgent(
name="memory_agent",
memory_blocks=[
CreateBlock(
label="persona",
value="You manage memory for the main agent. You are a background agent and you are not expected to respond to messages. When you receive a conversation snippet from the main thread, perform memory updates only if there are meaningful changes, and otherwise call the skip_memory_update tool.",
),
],
model="openai/gpt-4o-mini",
embedding="openai/text-embedding-ada-002",
include_base_tools=False,
include_base_tool_rules=False,
tools=BASE_MEMORY_TOOLS + [skip_memory_update.name],
tool_rules=[
TerminalToolRule(tool_name="core_memory_append"),
TerminalToolRule(tool_name="core_memory_replace"),
TerminalToolRule(tool_name="skip_memory_update"),
],
),
actor=actor,
)
# 4. create group
group = server.group_manager.create_group(
group=GroupCreate(
description="",
agent_ids=[background_memory_agent.id],
manager_config=BackgroundManager(
manager_agent_id=main_agent.id,
background_agents_interval=2,
),
shared_block_ids=[shared_memory_block.id],
),
actor=actor,
)
agents = server.block_manager.get_agents_for_block(block_id=shared_memory_block.id, actor=actor)
assert len(agents) == 2
message_text = [
"my favorite color is orange",
"not particularly. today is a good day",
"actually my favorite color is coral",
"sorry gotta run",
]
run_ids = []
for i, text in enumerate(message_text):
response = await server.send_message_to_agent(
agent_id=main_agent.id,
actor=actor,
messages=[
MessageCreate(
role="user",
content=text,
),
],
stream_steps=False,
stream_tokens=False,
)
assert len(response.messages) > 0
assert len(response.usage.run_ids) == i % 2
run_ids.extend(response.usage.run_ids)
time.sleep(5)
for run_id in run_ids:
job = server.job_manager.get_job_by_id(job_id=run_id, actor=actor)
assert job.status == JobStatus.completed
server.group_manager.delete_group(group_id=group.id, actor=actor)
server.agent_manager.delete_agent(agent_id=background_memory_agent.id, actor=actor)
server.agent_manager.delete_agent(agent_id=main_agent.id, actor=actor)