massgen 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of massgen might be problematic. Click here for more details.
- massgen/__init__.py +1 -1
- massgen/backend/base_with_custom_tool_and_mcp.py +453 -23
- massgen/backend/capabilities.py +39 -0
- massgen/backend/chat_completions.py +111 -197
- massgen/backend/claude.py +210 -181
- massgen/backend/gemini.py +1015 -1559
- massgen/backend/grok.py +3 -2
- massgen/backend/response.py +160 -220
- massgen/chat_agent.py +340 -20
- massgen/cli.py +399 -25
- massgen/config_builder.py +20 -54
- massgen/config_validator.py +931 -0
- massgen/configs/README.md +95 -10
- massgen/configs/memory/gpt5mini_gemini_baseline_research_to_implementation.yaml +94 -0
- massgen/configs/memory/gpt5mini_gemini_context_window_management.yaml +187 -0
- massgen/configs/memory/gpt5mini_gemini_research_to_implementation.yaml +127 -0
- massgen/configs/memory/gpt5mini_high_reasoning_gemini.yaml +107 -0
- massgen/configs/memory/single_agent_compression_test.yaml +64 -0
- massgen/configs/tools/custom_tools/claude_code_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/claude_custom_tool_example_no_path.yaml +1 -1
- massgen/configs/tools/custom_tools/claude_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/computer_use_browser_example.yaml +1 -1
- massgen/configs/tools/custom_tools/computer_use_docker_example.yaml +1 -1
- massgen/configs/tools/custom_tools/gemini_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/gpt_oss_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/grok3_mini_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/interop/ag2_and_langgraph_lesson_planner.yaml +65 -0
- massgen/configs/tools/custom_tools/interop/ag2_and_openai_assistant_lesson_planner.yaml +65 -0
- massgen/configs/tools/custom_tools/interop/ag2_lesson_planner_example.yaml +48 -0
- massgen/configs/tools/custom_tools/interop/agentscope_lesson_planner_example.yaml +48 -0
- massgen/configs/tools/custom_tools/interop/langgraph_lesson_planner_example.yaml +49 -0
- massgen/configs/tools/custom_tools/interop/openai_assistant_lesson_planner_example.yaml +50 -0
- massgen/configs/tools/custom_tools/interop/smolagent_lesson_planner_example.yaml +49 -0
- massgen/configs/tools/custom_tools/qwen_api_custom_tool_with_mcp_example.yaml +1 -0
- massgen/configs/tools/custom_tools/two_models_with_tools_example.yaml +44 -0
- massgen/formatter/_gemini_formatter.py +61 -15
- massgen/memory/README.md +277 -0
- massgen/memory/__init__.py +26 -0
- massgen/memory/_base.py +193 -0
- massgen/memory/_compression.py +237 -0
- massgen/memory/_context_monitor.py +211 -0
- massgen/memory/_conversation.py +255 -0
- massgen/memory/_fact_extraction_prompts.py +333 -0
- massgen/memory/_mem0_adapters.py +257 -0
- massgen/memory/_persistent.py +687 -0
- massgen/memory/docker-compose.qdrant.yml +36 -0
- massgen/memory/docs/DESIGN.md +388 -0
- massgen/memory/docs/QUICKSTART.md +409 -0
- massgen/memory/docs/SUMMARY.md +319 -0
- massgen/memory/docs/agent_use_memory.md +408 -0
- massgen/memory/docs/orchestrator_use_memory.md +586 -0
- massgen/memory/examples.py +237 -0
- massgen/orchestrator.py +207 -7
- massgen/tests/memory/test_agent_compression.py +174 -0
- massgen/tests/memory/test_context_window_management.py +286 -0
- massgen/tests/memory/test_force_compression.py +154 -0
- massgen/tests/memory/test_simple_compression.py +147 -0
- massgen/tests/test_ag2_lesson_planner.py +223 -0
- massgen/tests/test_agent_memory.py +534 -0
- massgen/tests/test_config_validator.py +1156 -0
- massgen/tests/test_conversation_memory.py +382 -0
- massgen/tests/test_langgraph_lesson_planner.py +223 -0
- massgen/tests/test_orchestrator_memory.py +620 -0
- massgen/tests/test_persistent_memory.py +435 -0
- massgen/token_manager/token_manager.py +6 -0
- massgen/tool/__init__.py +2 -9
- massgen/tool/_decorators.py +52 -0
- massgen/tool/_extraframework_agents/ag2_lesson_planner_tool.py +251 -0
- massgen/tool/_extraframework_agents/agentscope_lesson_planner_tool.py +303 -0
- massgen/tool/_extraframework_agents/langgraph_lesson_planner_tool.py +275 -0
- massgen/tool/_extraframework_agents/openai_assistant_lesson_planner_tool.py +247 -0
- massgen/tool/_extraframework_agents/smolagent_lesson_planner_tool.py +180 -0
- massgen/tool/_manager.py +102 -16
- massgen/tool/_registered_tool.py +3 -0
- massgen/tool/_result.py +3 -0
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/METADATA +138 -77
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/RECORD +82 -37
- massgen/backend/gemini_mcp_manager.py +0 -545
- massgen/backend/gemini_trackers.py +0 -344
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/WHEEL +0 -0
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Tests for Agent Memory Integration.
|
|
5
|
+
|
|
6
|
+
This module tests the integration of memory systems (ConversationMemory and
|
|
7
|
+
PersistentMemory) with MassGen agents (SingleAgent and ConfigurableAgent).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
# Import agent classes
|
|
15
|
+
from massgen.chat_agent import ConfigurableAgent, SingleAgent
|
|
16
|
+
from massgen.memory import ConversationMemory, PersistentMemory
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Helper functions for mocking
|
|
20
|
+
def create_mock_backend():
|
|
21
|
+
"""Create a mock LLM backend for testing."""
|
|
22
|
+
backend = MagicMock()
|
|
23
|
+
backend.is_stateful = MagicMock(return_value=False)
|
|
24
|
+
backend.set_stage = MagicMock()
|
|
25
|
+
|
|
26
|
+
# Mock stream_with_tools to return an async generator
|
|
27
|
+
async def mock_stream():
|
|
28
|
+
# Simulate assistant response
|
|
29
|
+
yield MagicMock(type="content", content="Hello! ")
|
|
30
|
+
yield MagicMock(type="content", content="How can I help?")
|
|
31
|
+
yield MagicMock(
|
|
32
|
+
type="complete_message",
|
|
33
|
+
complete_message={"role": "assistant", "content": "Hello! How can I help?"},
|
|
34
|
+
)
|
|
35
|
+
yield MagicMock(type="done")
|
|
36
|
+
|
|
37
|
+
backend.stream_with_tools = MagicMock(return_value=mock_stream())
|
|
38
|
+
return backend
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_mock_persistent_memory():
|
|
42
|
+
"""Create a mock persistent memory for testing."""
|
|
43
|
+
memory = MagicMock(spec=PersistentMemory)
|
|
44
|
+
memory.record = AsyncMock()
|
|
45
|
+
memory.retrieve = AsyncMock(return_value="Previous context: User likes Python")
|
|
46
|
+
return memory
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.asyncio
|
|
50
|
+
class TestSingleAgentConversationMemory:
|
|
51
|
+
"""Tests for SingleAgent with ConversationMemory."""
|
|
52
|
+
|
|
53
|
+
async def test_agent_with_conversation_memory_initialization(self):
|
|
54
|
+
"""Test that agent initializes correctly with conversation memory."""
|
|
55
|
+
backend = create_mock_backend()
|
|
56
|
+
conv_memory = ConversationMemory()
|
|
57
|
+
|
|
58
|
+
agent = SingleAgent(
|
|
59
|
+
backend=backend,
|
|
60
|
+
agent_id="test_agent",
|
|
61
|
+
system_message="You are a helpful assistant",
|
|
62
|
+
conversation_memory=conv_memory,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
assert agent.conversation_memory is conv_memory
|
|
66
|
+
assert agent.persistent_memory is None
|
|
67
|
+
print("✅ Agent with conversation memory initialization works")
|
|
68
|
+
|
|
69
|
+
async def test_agent_adds_messages_to_conversation_memory(self):
|
|
70
|
+
"""Test that agent adds messages to conversation memory during chat."""
|
|
71
|
+
backend = create_mock_backend()
|
|
72
|
+
conv_memory = ConversationMemory()
|
|
73
|
+
|
|
74
|
+
agent = SingleAgent(
|
|
75
|
+
backend=backend,
|
|
76
|
+
agent_id="test_agent",
|
|
77
|
+
conversation_memory=conv_memory,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Initial memory should be empty
|
|
81
|
+
assert await conv_memory.size() == 0
|
|
82
|
+
|
|
83
|
+
# Chat with agent
|
|
84
|
+
messages = [{"role": "user", "content": "Hello, agent!"}]
|
|
85
|
+
response_chunks = []
|
|
86
|
+
async for chunk in agent.chat(messages):
|
|
87
|
+
response_chunks.append(chunk)
|
|
88
|
+
|
|
89
|
+
# Memory should now contain user message and assistant response
|
|
90
|
+
memory_size = await conv_memory.size()
|
|
91
|
+
assert memory_size >= 1 # At least the user message
|
|
92
|
+
|
|
93
|
+
stored_messages = await conv_memory.get_messages()
|
|
94
|
+
user_messages = [msg for msg in stored_messages if msg.get("role") == "user"]
|
|
95
|
+
assert len(user_messages) >= 1
|
|
96
|
+
assert user_messages[0]["content"] == "Hello, agent!"
|
|
97
|
+
|
|
98
|
+
print("✅ Agent adds messages to conversation memory")
|
|
99
|
+
|
|
100
|
+
async def test_agent_clears_conversation_memory_on_reset(self):
|
|
101
|
+
"""Test that agent clears conversation memory when reset."""
|
|
102
|
+
backend = create_mock_backend()
|
|
103
|
+
conv_memory = ConversationMemory()
|
|
104
|
+
|
|
105
|
+
agent = SingleAgent(
|
|
106
|
+
backend=backend,
|
|
107
|
+
conversation_memory=conv_memory,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Add some messages
|
|
111
|
+
messages = [{"role": "user", "content": "Test message"}]
|
|
112
|
+
async for _ in agent.chat(messages):
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
# Memory should have content
|
|
116
|
+
assert await conv_memory.size() > 0
|
|
117
|
+
|
|
118
|
+
# Reset agent
|
|
119
|
+
await agent.reset()
|
|
120
|
+
|
|
121
|
+
# Memory should be cleared
|
|
122
|
+
assert await conv_memory.size() == 0
|
|
123
|
+
print("✅ Agent clears conversation memory on reset")
|
|
124
|
+
|
|
125
|
+
async def test_agent_clears_memory_on_clear_history(self):
|
|
126
|
+
"""Test that agent clears memory when clear_history flag is set."""
|
|
127
|
+
backend = create_mock_backend()
|
|
128
|
+
conv_memory = ConversationMemory()
|
|
129
|
+
|
|
130
|
+
agent = SingleAgent(
|
|
131
|
+
backend=backend,
|
|
132
|
+
conversation_memory=conv_memory,
|
|
133
|
+
system_message="System prompt",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# First conversation
|
|
137
|
+
messages1 = [{"role": "user", "content": "First message"}]
|
|
138
|
+
async for _ in agent.chat(messages1):
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
initial_size = await conv_memory.size()
|
|
142
|
+
assert initial_size > 0
|
|
143
|
+
|
|
144
|
+
# Clear and start new conversation
|
|
145
|
+
messages2 = [{"role": "user", "content": "Second message"}]
|
|
146
|
+
async for _ in agent.chat(messages2, clear_history=True):
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
# Memory should be cleared and only contain new messages
|
|
150
|
+
stored = await conv_memory.get_messages()
|
|
151
|
+
user_msgs = [m for m in stored if m.get("role") == "user"]
|
|
152
|
+
assert len(user_msgs) == 1
|
|
153
|
+
assert user_msgs[0]["content"] == "Second message"
|
|
154
|
+
|
|
155
|
+
print("✅ Agent clears memory on clear_history")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.mark.asyncio
|
|
159
|
+
class TestSingleAgentPersistentMemory:
|
|
160
|
+
"""Tests for SingleAgent with PersistentMemory."""
|
|
161
|
+
|
|
162
|
+
async def test_agent_with_persistent_memory_initialization(self):
|
|
163
|
+
"""Test that agent initializes correctly with persistent memory."""
|
|
164
|
+
backend = create_mock_backend()
|
|
165
|
+
persist_memory = create_mock_persistent_memory()
|
|
166
|
+
|
|
167
|
+
agent = SingleAgent(
|
|
168
|
+
backend=backend,
|
|
169
|
+
agent_id="test_agent",
|
|
170
|
+
persistent_memory=persist_memory,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert agent.persistent_memory is persist_memory
|
|
174
|
+
print("✅ Agent with persistent memory initialization works")
|
|
175
|
+
|
|
176
|
+
async def test_agent_retrieves_from_persistent_memory(self):
|
|
177
|
+
"""Test that agent retrieves context from persistent memory."""
|
|
178
|
+
backend = create_mock_backend()
|
|
179
|
+
persist_memory = create_mock_persistent_memory()
|
|
180
|
+
|
|
181
|
+
# Mock retrieve to return some context
|
|
182
|
+
persist_memory.retrieve = AsyncMock(
|
|
183
|
+
return_value="User previously asked about Python",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
agent = SingleAgent(
|
|
187
|
+
backend=backend,
|
|
188
|
+
persistent_memory=persist_memory,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Chat with agent
|
|
192
|
+
messages = [{"role": "user", "content": "Tell me more"}]
|
|
193
|
+
async for _ in agent.chat(messages):
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
# Verify retrieve was called
|
|
197
|
+
assert persist_memory.retrieve.called
|
|
198
|
+
print("✅ Agent retrieves from persistent memory")
|
|
199
|
+
|
|
200
|
+
async def test_agent_records_to_persistent_memory(self):
|
|
201
|
+
"""Test that agent records responses to persistent memory."""
|
|
202
|
+
backend = create_mock_backend()
|
|
203
|
+
persist_memory = create_mock_persistent_memory()
|
|
204
|
+
|
|
205
|
+
agent = SingleAgent(
|
|
206
|
+
backend=backend,
|
|
207
|
+
persistent_memory=persist_memory,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Chat with agent
|
|
211
|
+
messages = [{"role": "user", "content": "Remember this"}]
|
|
212
|
+
async for _ in agent.chat(messages):
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
# Verify record was called
|
|
216
|
+
assert persist_memory.record.called
|
|
217
|
+
print("✅ Agent records to persistent memory")
|
|
218
|
+
|
|
219
|
+
async def test_agent_handles_memory_not_implemented_gracefully(self):
|
|
220
|
+
"""Test that agent handles NotImplementedError from memory gracefully."""
|
|
221
|
+
backend = create_mock_backend()
|
|
222
|
+
persist_memory = MagicMock()
|
|
223
|
+
|
|
224
|
+
# Mock retrieve to raise NotImplementedError
|
|
225
|
+
persist_memory.retrieve = AsyncMock(side_effect=NotImplementedError())
|
|
226
|
+
persist_memory.record = AsyncMock(side_effect=NotImplementedError())
|
|
227
|
+
|
|
228
|
+
agent = SingleAgent(
|
|
229
|
+
backend=backend,
|
|
230
|
+
persistent_memory=persist_memory,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Should not raise error
|
|
234
|
+
messages = [{"role": "user", "content": "Test"}]
|
|
235
|
+
async for _ in agent.chat(messages):
|
|
236
|
+
pass
|
|
237
|
+
|
|
238
|
+
print("✅ Agent handles NotImplementedError gracefully")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.mark.asyncio
|
|
242
|
+
class TestSingleAgentBothMemories:
|
|
243
|
+
"""Tests for SingleAgent with both ConversationMemory and PersistentMemory."""
|
|
244
|
+
|
|
245
|
+
async def test_agent_with_both_memories(self):
|
|
246
|
+
"""Test that agent works correctly with both memory types."""
|
|
247
|
+
backend = create_mock_backend()
|
|
248
|
+
conv_memory = ConversationMemory()
|
|
249
|
+
persist_memory = create_mock_persistent_memory()
|
|
250
|
+
|
|
251
|
+
agent = SingleAgent(
|
|
252
|
+
backend=backend,
|
|
253
|
+
conversation_memory=conv_memory,
|
|
254
|
+
persistent_memory=persist_memory,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Chat with agent
|
|
258
|
+
messages = [{"role": "user", "content": "Hello with both memories"}]
|
|
259
|
+
async for _ in agent.chat(messages):
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
# Both memories should be used
|
|
263
|
+
assert await conv_memory.size() > 0
|
|
264
|
+
assert persist_memory.retrieve.called
|
|
265
|
+
assert persist_memory.record.called
|
|
266
|
+
|
|
267
|
+
print("✅ Agent works with both memory types")
|
|
268
|
+
|
|
269
|
+
async def test_memory_integration_flow(self):
|
|
270
|
+
"""Test complete memory integration flow."""
|
|
271
|
+
|
|
272
|
+
# Create a fresh backend for each chat call
|
|
273
|
+
def create_fresh_backend():
|
|
274
|
+
backend = MagicMock()
|
|
275
|
+
backend.is_stateful = MagicMock(return_value=False)
|
|
276
|
+
backend.set_stage = MagicMock()
|
|
277
|
+
|
|
278
|
+
async def mock_stream():
|
|
279
|
+
yield MagicMock(type="content", content="Response")
|
|
280
|
+
yield MagicMock(
|
|
281
|
+
type="complete_message",
|
|
282
|
+
complete_message={"role": "assistant", "content": "Response"},
|
|
283
|
+
)
|
|
284
|
+
yield MagicMock(type="done")
|
|
285
|
+
|
|
286
|
+
backend.stream_with_tools = MagicMock(return_value=mock_stream())
|
|
287
|
+
return backend
|
|
288
|
+
|
|
289
|
+
conv_memory = ConversationMemory()
|
|
290
|
+
persist_memory = create_mock_persistent_memory()
|
|
291
|
+
|
|
292
|
+
agent = SingleAgent(
|
|
293
|
+
backend=create_fresh_backend(),
|
|
294
|
+
agent_id="test_agent",
|
|
295
|
+
conversation_memory=conv_memory,
|
|
296
|
+
persistent_memory=persist_memory,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# First conversation
|
|
300
|
+
messages1 = [{"role": "user", "content": "My name is Alice"}]
|
|
301
|
+
async for _ in agent.chat(messages1):
|
|
302
|
+
pass
|
|
303
|
+
|
|
304
|
+
# Verify conversation memory has messages
|
|
305
|
+
conv_size1 = await conv_memory.size()
|
|
306
|
+
assert conv_size1 > 0
|
|
307
|
+
|
|
308
|
+
# Verify persistent memory recorded
|
|
309
|
+
assert persist_memory.record.called
|
|
310
|
+
record_call_count = persist_memory.record.call_count
|
|
311
|
+
|
|
312
|
+
# Update backend for second chat
|
|
313
|
+
agent.backend = create_fresh_backend()
|
|
314
|
+
|
|
315
|
+
# Second conversation
|
|
316
|
+
messages2 = [{"role": "user", "content": "What's my name?"}]
|
|
317
|
+
async for _ in agent.chat(messages2):
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
# Conversation memory should have grown
|
|
321
|
+
conv_size2 = await conv_memory.size()
|
|
322
|
+
assert conv_size2 > conv_size1
|
|
323
|
+
|
|
324
|
+
# Persistent memory should have been queried and recorded again
|
|
325
|
+
assert persist_memory.retrieve.call_count >= 1
|
|
326
|
+
assert persist_memory.record.call_count >= record_call_count
|
|
327
|
+
|
|
328
|
+
print("✅ Complete memory integration flow works")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@pytest.mark.asyncio
|
|
332
|
+
class TestConfigurableAgentMemory:
|
|
333
|
+
"""Tests for ConfigurableAgent with memory."""
|
|
334
|
+
|
|
335
|
+
async def test_configurable_agent_with_memory(self):
|
|
336
|
+
"""Test that ConfigurableAgent works with memory."""
|
|
337
|
+
from massgen.agent_config import AgentConfig
|
|
338
|
+
|
|
339
|
+
backend = create_mock_backend()
|
|
340
|
+
conv_memory = ConversationMemory()
|
|
341
|
+
persist_memory = create_mock_persistent_memory()
|
|
342
|
+
|
|
343
|
+
config = AgentConfig(
|
|
344
|
+
agent_id="configurable_test",
|
|
345
|
+
backend_params={"model": "gpt-4o-mini"},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
agent = ConfigurableAgent(
|
|
349
|
+
config=config,
|
|
350
|
+
backend=backend,
|
|
351
|
+
conversation_memory=conv_memory,
|
|
352
|
+
persistent_memory=persist_memory,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
assert agent.conversation_memory is conv_memory
|
|
356
|
+
assert agent.persistent_memory is persist_memory
|
|
357
|
+
|
|
358
|
+
# Test chat
|
|
359
|
+
messages = [{"role": "user", "content": "Test configurable"}]
|
|
360
|
+
async for _ in agent.chat(messages):
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
# Verify memory was used
|
|
364
|
+
assert await conv_memory.size() > 0
|
|
365
|
+
assert persist_memory.retrieve.called
|
|
366
|
+
|
|
367
|
+
print("✅ ConfigurableAgent works with memory")
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@pytest.mark.asyncio
|
|
371
|
+
class TestMemoryStateManagement:
|
|
372
|
+
"""Tests for memory state management in agents."""
|
|
373
|
+
|
|
374
|
+
async def test_conversation_memory_survives_across_chats(self):
|
|
375
|
+
"""Test that conversation memory persists across multiple chat calls."""
|
|
376
|
+
backend = create_mock_backend()
|
|
377
|
+
conv_memory = ConversationMemory()
|
|
378
|
+
|
|
379
|
+
agent = SingleAgent(
|
|
380
|
+
backend=backend,
|
|
381
|
+
conversation_memory=conv_memory,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# First chat
|
|
385
|
+
messages1 = [{"role": "user", "content": "First question"}]
|
|
386
|
+
async for _ in agent.chat(messages1):
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
size_after_first = await conv_memory.size()
|
|
390
|
+
|
|
391
|
+
# Second chat
|
|
392
|
+
messages2 = [{"role": "user", "content": "Second question"}]
|
|
393
|
+
async for _ in agent.chat(messages2):
|
|
394
|
+
pass
|
|
395
|
+
|
|
396
|
+
size_after_second = await conv_memory.size()
|
|
397
|
+
|
|
398
|
+
# Memory should accumulate
|
|
399
|
+
assert size_after_second > size_after_first
|
|
400
|
+
|
|
401
|
+
# Should have both messages
|
|
402
|
+
all_messages = await conv_memory.get_messages()
|
|
403
|
+
user_messages = [m for m in all_messages if m.get("role") == "user"]
|
|
404
|
+
assert len(user_messages) >= 2
|
|
405
|
+
|
|
406
|
+
print("✅ Conversation memory persists across chats")
|
|
407
|
+
|
|
408
|
+
async def test_reset_chat_clears_conversation_memory(self):
|
|
409
|
+
"""Test that reset_chat flag clears conversation memory."""
|
|
410
|
+
backend = create_mock_backend()
|
|
411
|
+
conv_memory = ConversationMemory()
|
|
412
|
+
|
|
413
|
+
agent = SingleAgent(
|
|
414
|
+
backend=backend,
|
|
415
|
+
conversation_memory=conv_memory,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
# First chat
|
|
419
|
+
messages1 = [{"role": "user", "content": "First"}]
|
|
420
|
+
async for _ in agent.chat(messages1):
|
|
421
|
+
pass
|
|
422
|
+
|
|
423
|
+
assert await conv_memory.size() > 0
|
|
424
|
+
|
|
425
|
+
# Reset chat
|
|
426
|
+
messages2 = [{"role": "user", "content": "Second"}]
|
|
427
|
+
async for _ in agent.chat(messages2, reset_chat=True):
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
# Memory should be reset
|
|
431
|
+
all_messages = await conv_memory.get_messages()
|
|
432
|
+
user_messages = [m for m in all_messages if m.get("role") == "user"]
|
|
433
|
+
|
|
434
|
+
# Should only have the reset message
|
|
435
|
+
assert len(user_messages) == 1
|
|
436
|
+
assert user_messages[0]["content"] == "Second"
|
|
437
|
+
|
|
438
|
+
print("✅ reset_chat clears conversation memory")
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@pytest.mark.asyncio
|
|
442
|
+
class TestMemoryErrorHandling:
|
|
443
|
+
"""Tests for error handling in memory operations."""
|
|
444
|
+
|
|
445
|
+
async def test_agent_continues_when_memory_add_fails(self):
|
|
446
|
+
"""Test that agent continues working when memory add fails."""
|
|
447
|
+
backend = create_mock_backend()
|
|
448
|
+
conv_memory = MagicMock(spec=ConversationMemory)
|
|
449
|
+
conv_memory.add = AsyncMock(side_effect=Exception("Memory error"))
|
|
450
|
+
conv_memory.clear = AsyncMock()
|
|
451
|
+
|
|
452
|
+
agent = SingleAgent(
|
|
453
|
+
backend=backend,
|
|
454
|
+
conversation_memory=conv_memory,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Should not crash even if memory fails
|
|
458
|
+
messages = [{"role": "user", "content": "Test"}]
|
|
459
|
+
chunks = []
|
|
460
|
+
async for chunk in agent.chat(messages):
|
|
461
|
+
chunks.append(chunk)
|
|
462
|
+
|
|
463
|
+
# Should have received response
|
|
464
|
+
assert len(chunks) > 0
|
|
465
|
+
print("✅ Agent continues when memory add fails")
|
|
466
|
+
|
|
467
|
+
async def test_agent_without_memory_works_normally(self):
|
|
468
|
+
"""Test that agent works normally without any memory."""
|
|
469
|
+
backend = create_mock_backend()
|
|
470
|
+
|
|
471
|
+
agent = SingleAgent(backend=backend)
|
|
472
|
+
|
|
473
|
+
assert agent.conversation_memory is None
|
|
474
|
+
assert agent.persistent_memory is None
|
|
475
|
+
|
|
476
|
+
# Should work without memory
|
|
477
|
+
messages = [{"role": "user", "content": "Test"}]
|
|
478
|
+
chunks = []
|
|
479
|
+
async for chunk in agent.chat(messages):
|
|
480
|
+
chunks.append(chunk)
|
|
481
|
+
|
|
482
|
+
assert len(chunks) > 0
|
|
483
|
+
print("✅ Agent works without memory")
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
if __name__ == "__main__":
|
|
487
|
+
import asyncio
|
|
488
|
+
|
|
489
|
+
async def run_all_tests():
|
|
490
|
+
"""Run all tests manually."""
|
|
491
|
+
print("\n=== Running Agent Memory Integration Tests ===\n")
|
|
492
|
+
|
|
493
|
+
# SingleAgent with ConversationMemory tests
|
|
494
|
+
print("\n--- SingleAgent with ConversationMemory ---")
|
|
495
|
+
test_conv = TestSingleAgentConversationMemory()
|
|
496
|
+
await test_conv.test_agent_with_conversation_memory_initialization()
|
|
497
|
+
await test_conv.test_agent_adds_messages_to_conversation_memory()
|
|
498
|
+
await test_conv.test_agent_clears_conversation_memory_on_reset()
|
|
499
|
+
await test_conv.test_agent_clears_memory_on_clear_history()
|
|
500
|
+
|
|
501
|
+
# SingleAgent with PersistentMemory tests
|
|
502
|
+
print("\n--- SingleAgent with PersistentMemory ---")
|
|
503
|
+
test_persist = TestSingleAgentPersistentMemory()
|
|
504
|
+
await test_persist.test_agent_with_persistent_memory_initialization()
|
|
505
|
+
await test_persist.test_agent_retrieves_from_persistent_memory()
|
|
506
|
+
await test_persist.test_agent_records_to_persistent_memory()
|
|
507
|
+
await test_persist.test_agent_handles_memory_not_implemented_gracefully()
|
|
508
|
+
|
|
509
|
+
# Both memories tests
|
|
510
|
+
print("\n--- SingleAgent with Both Memories ---")
|
|
511
|
+
test_both = TestSingleAgentBothMemories()
|
|
512
|
+
await test_both.test_agent_with_both_memories()
|
|
513
|
+
await test_both.test_memory_integration_flow()
|
|
514
|
+
|
|
515
|
+
# ConfigurableAgent tests
|
|
516
|
+
print("\n--- ConfigurableAgent with Memory ---")
|
|
517
|
+
test_config = TestConfigurableAgentMemory()
|
|
518
|
+
await test_config.test_configurable_agent_with_memory()
|
|
519
|
+
|
|
520
|
+
# State management tests
|
|
521
|
+
print("\n--- Memory State Management ---")
|
|
522
|
+
test_state = TestMemoryStateManagement()
|
|
523
|
+
await test_state.test_conversation_memory_survives_across_chats()
|
|
524
|
+
await test_state.test_reset_chat_clears_conversation_memory()
|
|
525
|
+
|
|
526
|
+
# Error handling tests
|
|
527
|
+
print("\n--- Memory Error Handling ---")
|
|
528
|
+
test_errors = TestMemoryErrorHandling()
|
|
529
|
+
await test_errors.test_agent_continues_when_memory_add_fails()
|
|
530
|
+
await test_errors.test_agent_without_memory_works_normally()
|
|
531
|
+
|
|
532
|
+
print("\n=== All Agent Memory Integration Tests Passed! ===\n")
|
|
533
|
+
|
|
534
|
+
asyncio.run(run_all_tests())
|