fix(core): stabilize system prompt refresh and expand git-memory coverage (#9438)

* fix(core): stabilize system prompt refresh and expand git-memory coverage

Only rebuild system prompts on explicit refresh paths so normal turns preserve prefix-cache stability, including git/custom prompt layouts. Add integration coverage for memory filesystem tree structure and recompile/reset system-message updates via message-id retrieval.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(core): recompile system prompt around compaction and stabilize source tests

Force system prompt refresh before/after compaction in LettaAgentV3 so repaired system+memory state is used and persisted across subsequent turns. Update source-system prompt tests to explicitly recompile before raw preview assertions instead of assuming automatic rebuild timing.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Sarah Wooders
2026-02-11 16:21:39 -08:00
committed by Caren Thomas
parent 5b7dd15905
commit d7793a4474
8 changed files with 105 additions and 107 deletions

View File

@@ -333,7 +333,7 @@ Some directory content
)
result = ContextWindowCalculator.extract_system_components(system_message)
# memory_filesystem should be the tree view only
# memory_filesystem should preserve tree connectors with deterministic ordering
assert result["memory_filesystem"] is not None
assert "\u251c\u2500\u2500 system/" in result["memory_filesystem"]

View File

@@ -27,8 +27,20 @@ from tests.utils import wait_for_server
SERVER_PORT = 8283
def get_raw_system_message(client: LettaSDKClient, agent_id: str) -> str:
def recompile_agent_system_prompt(client: LettaSDKClient, agent_id: str) -> None:
"""Force a system prompt recompilation for deterministic raw-preview assertions."""
client.post(
f"/v1/agents/{agent_id}/recompile",
cast_to=str,
body={},
)
def get_raw_system_message(client: LettaSDKClient, agent_id: str, recompile: bool = False) -> str:
"""Helper function to get the raw system message from an agent's preview payload."""
if recompile:
recompile_agent_system_prompt(client, agent_id)
raw_payload = client.post(
f"/v1/agents/{agent_id}/messages/preview-raw-payload",
cast_to=dict[str, Any],
@@ -215,7 +227,7 @@ def test_file_upload_creates_source_blocks_correctly(
assert any(re.fullmatch(expected_label_regex, b.label) for b in blocks)
# verify raw system message contains source information
raw_system_message = get_raw_system_message(client, agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id, recompile=True)
assert "test_source" in raw_system_message
assert "<directories>" in raw_system_message
# verify file-specific details in raw system message
@@ -234,7 +246,7 @@ def test_file_upload_creates_source_blocks_correctly(
assert not any(re.fullmatch(expected_label_regex, b.label) for b in blocks)
# verify raw system message no longer contains source information
raw_system_message_after_removal = get_raw_system_message(client, agent_state.id)
raw_system_message_after_removal = get_raw_system_message(client, agent_state.id, recompile=True)
# this should be in, because we didn't delete the source
assert "test_source" in raw_system_message_after_removal
assert "<directories>" in raw_system_message_after_removal
@@ -266,7 +278,7 @@ def test_attach_existing_files_creates_source_blocks_correctly(
# Attach after uploading the file
client.agents.folders.attach(folder_id=source.id, agent_id=agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id, recompile=True)
# Assert that the expected chunk is in the raw system message
expected_chunk = """<directories>
@@ -307,7 +319,7 @@ def test_attach_existing_files_creates_source_blocks_correctly(
assert not any("test" in b.value for b in blocks)
# Verify no traces of the prompt exist in the raw system message after detaching
raw_system_message_after_detach = get_raw_system_message(client, agent_state.id)
raw_system_message_after_detach = get_raw_system_message(client, agent_state.id, recompile=True)
assert expected_chunk not in raw_system_message_after_detach
assert "test_source" not in raw_system_message_after_detach
assert "<directories>" not in raw_system_message_after_detach
@@ -321,7 +333,7 @@ def test_delete_source_removes_source_blocks_correctly(
assert len(list(client.folders.list())) == 1
client.agents.folders.attach(folder_id=source.id, agent_id=agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id, recompile=True)
assert "test_source" in raw_system_message
assert "<directories>" in raw_system_message
@@ -330,7 +342,7 @@ def test_delete_source_removes_source_blocks_correctly(
# Upload the files
upload_file_and_wait(client, source.id, file_path)
raw_system_message = get_raw_system_message(client, agent_state.id)
raw_system_message = get_raw_system_message(client, agent_state.id, recompile=True)
# Assert that the expected chunk is in the raw system message
expected_chunk = """<directories>
<file_limits>
@@ -361,7 +373,7 @@ def test_delete_source_removes_source_blocks_correctly(
# Remove file from source
client.folders.delete(folder_id=source.id)
raw_system_message_after_detach = get_raw_system_message(client, agent_state.id)
raw_system_message_after_detach = get_raw_system_message(client, agent_state.id, recompile=True)
assert expected_chunk not in raw_system_message_after_detach
assert "test_source" not in raw_system_message_after_detach
assert "<directories>" not in raw_system_message_after_detach
@@ -1112,7 +1124,7 @@ def test_agent_open_file(disable_pinecone, disable_turbopuffer, client: LettaSDK
closed_files = client.agents.files.open(agent_id=agent_state.id, file_id=file_metadata["id"])
assert len(closed_files) == 0
system = get_raw_system_message(client, agent_state.id)
system = get_raw_system_message(client, agent_state.id, recompile=True)
assert '<file status="open" name="test_source/test.txt">' in system
assert "[Viewing file start (out of 1 lines)]" in system
@@ -1137,7 +1149,7 @@ def test_agent_close_file(disable_pinecone, disable_turbopuffer, client: LettaSD
# Test close_file function
client.agents.files.close(agent_id=agent_state.id, file_id=file_metadata["id"])
system = get_raw_system_message(client, agent_state.id)
system = get_raw_system_message(client, agent_state.id, recompile=True)
assert '<file status="closed" name="test_source/test.txt">' in system
@@ -1160,7 +1172,7 @@ def test_agent_close_all_open_files(disable_pinecone, disable_turbopuffer, clien
# Open each file
client.agents.files.open(agent_id=agent_state.id, file_id=file_metadata["id"])
system = get_raw_system_message(client, agent_state.id)
system = get_raw_system_message(client, agent_state.id, recompile=True)
assert '<file status="open"' in system
# Test close_all_open_files function
@@ -1170,7 +1182,7 @@ def test_agent_close_all_open_files(disable_pinecone, disable_turbopuffer, clien
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert all(isinstance(item, str) for item in result), "All items in result should be strings"
system = get_raw_system_message(client, agent_state.id)
system = get_raw_system_message(client, agent_state.id, recompile=True)
assert '<file status="open"' not in system