mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-07 08:26:06 +00:00
* refactor(core): replace text truncation utilities with new marker system - Remove old truncate_text_utils module and its exports - Replace TRUNCATION_MARKER_START with _TRUNCATION_NOTICE_MARKER constant - Update as_msg_stat.py to split content using new marker format - Modify FileIO tool to use TRUNCATION_NOTICE_MARKER for continuation hints - Change is_truncated function checks to use marker presence detection - Move transformers dependency from main deps to light extra dependencies - Update tool result compactor tests to verify marker instead of is_truncated calls * feat(file_io): enhance file operations with path resolution and append functionality - Add expanduser() to resolve file paths with ~ symbol - Implement proper file existence and type validation in update_file - Add new append_file method to append content to files - Update truncation notice format for better readability - Fix typo in error message from "provide" to "provided" - Update transformers dependency in pyproject.toml - Remove duplicate transformers dependency from light extras * refactor(file_io): disable pylint too-many-return-statements warning * perf(file_watcher): increase default polling delay and optimize watcher configuration - Increased default poll_delay_ms from 1000ms to 2000ms to reduce CPU usage - Removed force_polling parameter as it's no longer needed with updated polling strategy - Simplified async watch configuration by removing conditional force_polling logic - Reduced overall system resource consumption during file watching operations * refactor(memory): update conversation log documentation in memory summary - Changed "Raw conversation logs" to "Earlier conversation logs" for clarity - Added warning note about potentially large dialog file sizes - Improved formatting with additional line break for better readability - Maintained existing compressed summary integration unchanged * feat(memory): add long-term memory support to file-based memory system - Initialize _long_term_memory attribute as empty string - Add memories section to content when long-term memory exists - Consolidate summary and memories into single user message - Format memories with markdown header # Memories - Maintain existing compressed summary functionality - Join multiple content parts with double newlines
176 lines
7 KiB
Python
176 lines
7 KiB
Python
"""Tests for ToolResultCompactor."""
|
|
|
|
import asyncio
|
|
import tempfile
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
from agentscope.message import Msg
|
|
|
|
from reme.memory.file_based.components import ToolResultCompactor
|
|
from reme.memory.file_based.utils import TRUNCATION_NOTICE_MARKER
|
|
|
|
|
|
def create_tool_result_msg(output: str | list, tool_name: str = "test_tool") -> Msg:
|
|
"""Create a Msg with tool_result content block."""
|
|
return Msg(
|
|
name="tool",
|
|
role="user",
|
|
content=[
|
|
{
|
|
"type": "tool_result",
|
|
"id": "call_123",
|
|
"name": tool_name,
|
|
"output": output,
|
|
},
|
|
],
|
|
)
|
|
|
|
|
|
class TestToolResultCompactor:
|
|
"""Tests for ToolResultCompactor."""
|
|
|
|
def test_no_truncation_when_under_threshold(self):
|
|
"""Test that short content is not truncated."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=1000)
|
|
messages = [create_tool_result_msg("short content")]
|
|
|
|
result = asyncio.run(op.call(messages=messages))
|
|
|
|
assert result == messages
|
|
assert messages[0].content[0]["output"] == "short content"
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 0
|
|
|
|
def test_truncation_when_over_threshold(self):
|
|
"""Test that long content is truncated and saved to file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
long_content = "x" * 500
|
|
messages = [create_tool_result_msg(long_content)]
|
|
|
|
_ = asyncio.run(op.call(messages=messages))
|
|
|
|
output = messages[0].content[0]["output"]
|
|
assert TRUNCATION_NOTICE_MARKER in output
|
|
assert "[Full content saved to:" in output
|
|
|
|
# Verify file was created
|
|
files = list(Path(tmpdir).glob("*.txt"))
|
|
assert len(files) == 1
|
|
|
|
# Verify file content
|
|
content = files[0].read_text()
|
|
assert "# tool_name: test_tool" in content
|
|
assert "# created_at:" in content
|
|
assert long_content in content
|
|
|
|
def test_skip_already_truncated(self):
|
|
"""Test that already truncated content is not re-truncated."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
truncated_content = "head<<<TRUNCATED>>>(100 chars omitted)<<<END_TRUNCATED>>>tail"
|
|
messages = [create_tool_result_msg(truncated_content)]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
assert messages[0].content[0]["output"] == truncated_content
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 0
|
|
|
|
def test_truncation_list_output(self):
|
|
"""Test truncation of list output with text blocks."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
list_output = [{"type": "text", "text": "y" * 500}]
|
|
messages = [create_tool_result_msg(list_output)]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
text_block = messages[0].content[0]["output"][0]
|
|
assert TRUNCATION_NOTICE_MARKER in text_block["text"]
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 1
|
|
|
|
def test_list_output_no_truncation_when_short(self):
|
|
"""Test that short list output is not truncated."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=1000)
|
|
list_output = [{"type": "text", "text": "short"}]
|
|
messages = [create_tool_result_msg(list_output)]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
assert messages[0].content[0]["output"][0]["text"] == "short"
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 0
|
|
|
|
def test_list_output_multiple_text_blocks(self):
|
|
"""Test truncation of multiple text blocks in list output."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
list_output = [
|
|
{"type": "text", "text": "a" * 500},
|
|
{"type": "text", "text": "short"},
|
|
{"type": "text", "text": "b" * 500},
|
|
]
|
|
messages = [create_tool_result_msg(list_output)]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
output = messages[0].content[0]["output"]
|
|
assert TRUNCATION_NOTICE_MARKER in output[0]["text"]
|
|
assert output[1]["text"] == "short" # unchanged
|
|
assert TRUNCATION_NOTICE_MARKER in output[2]["text"]
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 2
|
|
|
|
def test_list_output_mixed_block_types(self):
|
|
"""Test that non-text blocks in list output are unchanged."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
list_output = [
|
|
{"type": "text", "text": "c" * 500},
|
|
{"type": "image", "source": {"type": "url", "url": "http://example.com/img.png"}},
|
|
]
|
|
messages = [create_tool_result_msg(list_output)]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
output = messages[0].content[0]["output"]
|
|
assert TRUNCATION_NOTICE_MARKER in output[0]["text"]
|
|
assert output[1] == {"type": "image", "source": {"type": "url", "url": "http://example.com/img.png"}}
|
|
assert len(list(Path(tmpdir).glob("*.txt"))) == 1
|
|
|
|
def test_cleanup_expired_files(self):
|
|
"""Test cleanup of expired files."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100, retention_days=1)
|
|
|
|
# Create an old file
|
|
old_time = (datetime.now() - timedelta(days=2)).isoformat()
|
|
old_file = Path(tmpdir) / "old_file.txt"
|
|
old_file.write_text(f"# tool_name: test\n# created_at: {old_time}\n# ---\ncontent")
|
|
|
|
# Create a new file
|
|
new_time = datetime.now().isoformat()
|
|
new_file = Path(tmpdir) / "new_file.txt"
|
|
new_file.write_text(f"# tool_name: test\n# created_at: {new_time}\n# ---\ncontent")
|
|
|
|
deleted = op.cleanup_expired_files()
|
|
|
|
assert deleted == 1
|
|
assert not old_file.exists()
|
|
assert new_file.exists()
|
|
|
|
def test_string_content_msg_unchanged(self):
|
|
"""Test that messages with string content are unchanged."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
op = ToolResultCompactor(tool_result_dir=tmpdir, recent_max_bytes=100)
|
|
messages = [Msg(name="user", role="user", content="hello world")]
|
|
|
|
asyncio.run(op.call(messages=messages))
|
|
|
|
assert messages[0].content == "hello world"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import pytest
|
|
|
|
pytest.main([__file__, "-v"])
|