feat: update compact endpoint to store summary message (#9215)

* base

* add tests
This commit is contained in:
jnjpng
2026-01-30 12:38:32 -08:00
committed by Caren Thomas
parent 9471fab2cf
commit e25a0c9cdf
4 changed files with 103 additions and 0 deletions

View File

@@ -2250,6 +2250,7 @@ async def summarize_messages(
summary_message, messages, summary = await agent_loop.compact(
messages=in_context_messages,
compaction_settings=compaction_settings,
use_summary_role=True,
)
num_messages_after = len(messages)

View File

@@ -515,6 +515,7 @@ async def compact_conversation(
summary_message, messages, summary = await agent_loop.compact(
messages=in_context_messages,
compaction_settings=compaction_settings,
use_summary_role=True,
)
num_messages_after = len(messages)

View File

@@ -617,6 +617,45 @@ class TestConversationCompact:
)
assert len(compacted_messages) < initial_count
def test_compact_conversation_creates_summary_role_message(self, client: Letta, agent, server_url: str):
"""Test that compaction creates a summary message with role='summary'."""
# Create a conversation
conversation = client.conversations.create(agent_id=agent.id)
# Send multiple messages to create a history worth summarizing
for i in range(5):
list(
client.conversations.messages.create(
conversation_id=conversation.id,
messages=[{"role": "user", "content": f"Message {i}: Tell me about topic {i}."}],
)
)
# Call compact endpoint with 'all' mode to ensure a single summary
response = requests.post(
f"{server_url}/v1/conversations/{conversation.id}/compact",
json={
"compaction_settings": {
"mode": "all",
}
},
)
assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}"
# Get compacted messages
compacted_messages = client.conversations.messages.list(
conversation_id=conversation.id,
order="asc",
)
# After 'all' mode compaction, we expect: system message + summary message
# The summary message should have role='summary'
summary_messages = [msg for msg in compacted_messages if msg.role == "summary"]
assert len(summary_messages) == 1, (
f"Expected exactly 1 summary message after compaction, found {len(summary_messages)}. "
f"Message roles: {[msg.role for msg in compacted_messages]}"
)
def test_compact_conversation_with_settings(self, client: Letta, agent, server_url: str):
"""Test conversation compaction with custom compaction settings."""
# Create a conversation with multiple messages

View File

@@ -846,6 +846,68 @@ async def test_compact_returns_valid_summary_message_and_event_message(server: S
assert "context_window" in event_msg.event_data
@pytest.mark.asyncio
@pytest.mark.parametrize(
"llm_config",
TESTED_LLM_CONFIGS,
ids=[c.model for c in TESTED_LLM_CONFIGS],
)
async def test_compact_with_use_summary_role_creates_summary_message_role(server: SyncServer, actor, llm_config: LLMConfig):
"""
Test that compact() with use_summary_role=True creates a message with role=MessageRole.summary.
This validates that manual compaction endpoints (which pass use_summary_role=True)
will store summary messages with the dedicated 'summary' role instead of the legacy 'user' role.
"""
# Create a conversation with enough messages to summarize
messages = [
PydanticMessage(
role=MessageRole.system,
content=[TextContent(type="text", text="You are a helpful assistant.")],
)
]
for i in range(10):
messages.append(
PydanticMessage(
role=MessageRole.user,
content=[TextContent(type="text", text=f"User message {i}: Test message {i}.")],
)
)
messages.append(
PydanticMessage(
role=MessageRole.assistant,
content=[TextContent(type="text", text=f"Assistant response {i}: Acknowledged message {i}.")],
)
)
agent_state, in_context_messages = await create_agent_with_messages(server, actor, llm_config, messages)
handle = llm_config.handle or f"{llm_config.model_endpoint_type}/{llm_config.model}"
agent_state.compaction_settings = CompactionSettings(model=handle, mode="all")
agent_loop = LettaAgentV3(agent_state=agent_state, actor=actor)
# Call compact with use_summary_role=True (as the REST endpoints now do)
summary_message_obj, compacted_messages, summary_text = await agent_loop.compact(
messages=in_context_messages,
use_summary_role=True,
)
# Verify the summary message has role=summary (not user)
assert summary_message_obj.role == MessageRole.summary, (
f"Expected summary message to have role=summary when use_summary_role=True, got {summary_message_obj.role}"
)
# Verify the compacted messages list structure
assert len(compacted_messages) == 2, f"Expected 2 messages (system + summary), got {len(compacted_messages)}"
assert compacted_messages[0].role == MessageRole.system
assert compacted_messages[1].role == MessageRole.summary
# Verify summary text is non-empty
assert isinstance(summary_text, str)
assert len(summary_text) > 0
@pytest.mark.asyncio
async def test_v3_compact_uses_compaction_settings_model_and_model_settings(server: SyncServer, actor):
"""Integration test: LettaAgentV3.compact uses the LLMConfig implied by CompactionSettings.