|
| 1 | +# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= |
| 2 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | +# you may not use this file except in compliance with the License. |
| 4 | +# You may obtain a copy of the License at |
| 5 | +# |
| 6 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +# |
| 8 | +# Unless required by applicable law or agreed to in writing, software |
| 9 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | +# See the License for the specific language governing permissions and |
| 12 | +# limitations under the License. |
| 13 | +# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= |
| 14 | + |
| 15 | +import tempfile |
| 16 | +from pathlib import Path |
| 17 | +from unittest.mock import MagicMock |
| 18 | + |
| 19 | +import pytest |
| 20 | + |
| 21 | +from camel.agents import ChatAgent |
| 22 | +from camel.models import BaseModelBackend |
| 23 | +from camel.utils.context_utils import ContextUtility |
| 24 | + |
| 25 | + |
| 26 | +@pytest.fixture(scope="function") |
| 27 | +def temp_directory(): |
| 28 | + r"""Create a temporary directory for test files.""" |
| 29 | + with tempfile.TemporaryDirectory() as temp_dir: |
| 30 | + yield temp_dir |
| 31 | + |
| 32 | + |
| 33 | +@pytest.fixture(scope="function") |
| 34 | +def mock_agent(): |
| 35 | + r"""Create a mock ChatAgent for testing.""" |
| 36 | + agent = MagicMock(spec=ChatAgent) |
| 37 | + agent.agent_id = "test_agent_001" |
| 38 | + |
| 39 | + # Create a mock model backend |
| 40 | + mock_model = MagicMock(spec=BaseModelBackend) |
| 41 | + agent.model_backend = mock_model |
| 42 | + |
| 43 | + agent.update_memory = MagicMock() |
| 44 | + agent.clear_memory = MagicMock() |
| 45 | + return agent |
| 46 | + |
| 47 | + |
| 48 | +@pytest.fixture(scope="function") |
| 49 | +def context_utility_fixture(temp_directory): |
| 50 | + r"""Create a ContextUtility instance for testing.""" |
| 51 | + return ContextUtility(working_directory=temp_directory) |
| 52 | + |
| 53 | + |
| 54 | +def test_load_markdown_context_to_memory_preserves_existing_conversation( |
| 55 | + mock_agent, context_utility_fixture |
| 56 | +): |
| 57 | + r"""Test that load_markdown_context_to_memory preserves existing memory.""" |
| 58 | + context_util = context_utility_fixture |
| 59 | + |
| 60 | + # Create test context content |
| 61 | + context_content = """# Previous Workflow Summary |
| 62 | +
|
| 63 | +## Task Completed |
| 64 | +Data analysis of customer sales using pandas and matplotlib. |
| 65 | +
|
| 66 | +## Key Findings |
| 67 | +- Sales increased 15% in Q4 |
| 68 | +- Electronics is top category |
| 69 | +- Customer retention: 85% |
| 70 | +""" |
| 71 | + |
| 72 | + # Save context file using ContextUtility |
| 73 | + context_util.save_markdown_file( |
| 74 | + filename="test_workflow", |
| 75 | + content=context_content, |
| 76 | + title="Test Workflow Context", |
| 77 | + ) |
| 78 | + |
| 79 | + # Load context into agent memory |
| 80 | + result = context_util.load_markdown_context_to_memory( |
| 81 | + agent=mock_agent, filename="test_workflow" |
| 82 | + ) |
| 83 | + |
| 84 | + # Verify successful loading |
| 85 | + assert "Context appended to agent" in result |
| 86 | + assert "characters)" in result |
| 87 | + |
| 88 | + # Verify that update_memory was called (but NOT clear_memory) |
| 89 | + mock_agent.update_memory.assert_called_once() |
| 90 | + mock_agent.clear_memory.assert_not_called() |
| 91 | + |
| 92 | + # Check the message content and role |
| 93 | + call_args = mock_agent.update_memory.call_args |
| 94 | + context_message = call_args[0][0] # First argument (message) |
| 95 | + backend_role = call_args[0][1] # Second argument (role) |
| 96 | + |
| 97 | + # Verify the context message format |
| 98 | + assert ( |
| 99 | + "The following is the context from a previous" |
| 100 | + in context_message.content |
| 101 | + ) |
| 102 | + assert "Data analysis of customer sales" in context_message.content |
| 103 | + assert "Sales increased 15%" in context_message.content |
| 104 | + assert backend_role.value == "user" |
| 105 | + |
| 106 | + |
| 107 | +def test_load_markdown_context_to_memory_file_not_found( |
| 108 | + mock_agent, context_utility_fixture |
| 109 | +): |
| 110 | + r"""Test handling of non-existent context files.""" |
| 111 | + context_util = context_utility_fixture |
| 112 | + |
| 113 | + # Try to load non-existent file |
| 114 | + result = context_util.load_markdown_context_to_memory( |
| 115 | + agent=mock_agent, filename="nonexistent_file" |
| 116 | + ) |
| 117 | + |
| 118 | + # Verify error handling |
| 119 | + assert "Context file not found or empty" in result |
| 120 | + |
| 121 | + # Verify no memory operations occurred |
| 122 | + mock_agent.update_memory.assert_not_called() |
| 123 | + mock_agent.clear_memory.assert_not_called() |
| 124 | + |
| 125 | + |
| 126 | +def test_load_markdown_context_to_memory_empty_file( |
| 127 | + mock_agent, context_utility_fixture |
| 128 | +): |
| 129 | + r"""Test handling of empty context files.""" |
| 130 | + context_util = context_utility_fixture |
| 131 | + |
| 132 | + # Create truly empty file by writing directly to filesystem |
| 133 | + empty_file = context_util.working_directory / "empty_context.md" |
| 134 | + empty_file.write_text("", encoding="utf-8") |
| 135 | + |
| 136 | + # Try to load empty file |
| 137 | + result = context_util.load_markdown_context_to_memory( |
| 138 | + agent=mock_agent, filename="empty_context" |
| 139 | + ) |
| 140 | + |
| 141 | + # Verify error handling |
| 142 | + assert "Context file not found or empty" in result |
| 143 | + |
| 144 | + # Verify no memory operations occurred |
| 145 | + mock_agent.update_memory.assert_not_called() |
| 146 | + |
| 147 | + |
| 148 | +def test_load_markdown_context_to_memory_with_workflow_content( |
| 149 | + mock_agent, context_utility_fixture |
| 150 | +): |
| 151 | + r"""Test loading workflow context with multiple agents and tools.""" |
| 152 | + context_util = context_utility_fixture |
| 153 | + |
| 154 | + # Create workflow context |
| 155 | + workflow_content = """## Multi-Agent Workflow |
| 156 | +
|
| 157 | +### Agents Involved |
| 158 | +- Agent A: Data collection using web_toolkit |
| 159 | +- Agent B: Analysis using pandas_toolkit |
| 160 | +- Agent C: Reporting using email_toolkit |
| 161 | +
|
| 162 | +### Results |
| 163 | +Successfully processed 10,000 records and sent reports. |
| 164 | +
|
| 165 | +### Next Steps |
| 166 | +- Automate the pipeline |
| 167 | +- Add error handling |
| 168 | +""" |
| 169 | + |
| 170 | + # Save workflow context |
| 171 | + context_util.save_markdown_file( |
| 172 | + filename="multi_agent_workflow", content=workflow_content |
| 173 | + ) |
| 174 | + |
| 175 | + # Load into agent memory |
| 176 | + result = context_util.load_markdown_context_to_memory( |
| 177 | + agent=mock_agent, filename="multi_agent_workflow" |
| 178 | + ) |
| 179 | + |
| 180 | + # Verify successful loading |
| 181 | + assert "Context appended to agent" in result |
| 182 | + |
| 183 | + # Check that workflow details are preserved |
| 184 | + call_args = mock_agent.update_memory.call_args |
| 185 | + context_message = call_args[0][0] |
| 186 | + |
| 187 | + assert "Multi-Agent Workflow" in context_message.content |
| 188 | + assert "web_toolkit" in context_message.content |
| 189 | + assert "pandas_toolkit" in context_message.content |
| 190 | + assert "10,000 records" in context_message.content |
| 191 | + |
| 192 | + |
| 193 | +def test_context_utility_initialization(temp_directory): |
| 194 | + r"""Test ContextUtility initialization.""" |
| 195 | + context_util = ContextUtility(working_directory=temp_directory) |
| 196 | + |
| 197 | + assert ( |
| 198 | + context_util.working_directory.parent.resolve() |
| 199 | + == Path(temp_directory).resolve() |
| 200 | + ) |
| 201 | + assert context_util.session_id is not None |
| 202 | + assert context_util.working_directory.exists() |
| 203 | + |
| 204 | + |
| 205 | +def test_save_and_load_markdown_file(context_utility_fixture): |
| 206 | + r"""Test saving and loading markdown files.""" |
| 207 | + context_util = context_utility_fixture |
| 208 | + |
| 209 | + # Test content |
| 210 | + test_content = "This is test content for markdown file." |
| 211 | + |
| 212 | + # Save file |
| 213 | + result = context_util.save_markdown_file( |
| 214 | + filename="test_file", content=test_content, title="Test Title" |
| 215 | + ) |
| 216 | + |
| 217 | + assert "saved successfully" in result |
| 218 | + |
| 219 | + # Load file |
| 220 | + loaded_content = context_util.load_markdown_file("test_file") |
| 221 | + |
| 222 | + assert "Test Title" in loaded_content |
| 223 | + assert test_content in loaded_content |
| 224 | + |
| 225 | + |
| 226 | +if __name__ == "__main__": |
| 227 | + import sys |
| 228 | + |
| 229 | + pytest.main([sys.argv[0]]) |
0 commit comments