Spaces:
Sleeping
Sleeping
Nihar Shah
Initial commit: OpenEnv Business CRM Query Environment with memory system and multi-agent architecture
8f7a08e | """ | |
| Tests for memory system and temporal reasoning. | |
| """ | |
| import pytest | |
| from app.env import CRMQueryEnv | |
| from app.tasks import get_task_by_id | |
| from app.models import Observation | |
| class TestMemoryInitialization: | |
| """Test memory system initialization.""" | |
| def test_memory_fields_in_state(self): | |
| """Test that environment state has memory fields.""" | |
| env = CRMQueryEnv() | |
| obs = env.reset() | |
| assert hasattr(obs, 'memory_cache') | |
| assert hasattr(obs, 'step_summaries') | |
| assert isinstance(obs.memory_cache, dict) | |
| assert isinstance(obs.step_summaries, list) | |
| def test_retrieved_entities_initialization(self): | |
| """Test that retrieved entities are initialized.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| assert 'customers' in env.retrieved_entities | |
| assert 'orders' in env.retrieved_entities | |
| assert 'tickets' in env.retrieved_entities | |
| assert len(env.retrieved_entities['customers']) == 0 | |
| assert len(env.retrieved_entities['orders']) == 0 | |
| assert len(env.retrieved_entities['tickets']) == 0 | |
| def test_step_summaries_initialization(self): | |
| """Test that step summaries are initialized.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| assert isinstance(env.step_summaries, list) | |
| assert len(env.step_summaries) == 0 | |
| class TestEntityCaching: | |
| """Test entity caching and retrieval.""" | |
| def test_customers_cached_on_search(self): | |
| """Test that customers are cached when searched.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = { | |
| "tool": "search_customers", | |
| "arguments": {"tier": "Gold"} | |
| } | |
| obs, reward, done, info = env.step(action) | |
| assert len(env.retrieved_entities['customers']) > 0 | |
| assert len(obs.memory_cache['customers']) > 0 | |
| def test_orders_cached_on_search(self): | |
| """Test that orders are cached when searched.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = { | |
| "tool": "search_orders", | |
| "arguments": {"status": "Completed"} | |
| } | |
| obs, reward, done, info = env.step(action) | |
| assert len(env.retrieved_entities['orders']) > 0 | |
| assert len(obs.memory_cache['orders']) > 0 | |
| def test_tickets_cached_on_search(self): | |
| """Test that tickets are cached when searched.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = { | |
| "tool": "search_tickets", | |
| "arguments": {"priority": "High"} | |
| } | |
| obs, reward, done, info = env.step(action) | |
| assert len(env.retrieved_entities['tickets']) > 0 | |
| assert len(obs.memory_cache['tickets']) > 0 | |
| def test_multiple_queries_accumulate(self): | |
| """Test that multiple queries accumulate cached entities.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| # First query | |
| action1 = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| env.step(action1) | |
| count1 = len(env.retrieved_entities['customers']) | |
| # Second query | |
| action2 = {"tool": "search_customers", "arguments": {"tier": "Silver"}} | |
| env.step(action2) | |
| count2 = len(env.retrieved_entities['customers']) | |
| assert count2 >= count1 | |
| assert count2 > 0 | |
| class TestStepSummaries: | |
| """Test step summary generation.""" | |
| def test_summary_created_per_step(self): | |
| """Test that summary is created for each step.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| assert len(env.step_summaries) == 0 | |
| action = { | |
| "tool": "search_customers", | |
| "arguments": {"tier": "Gold"} | |
| } | |
| env.step(action) | |
| assert len(env.step_summaries) == 1 | |
| assert isinstance(env.step_summaries[0], str) | |
| assert "search_customers" in env.step_summaries[0] | |
| def test_summary_format(self): | |
| """Test that summaries have expected format.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = { | |
| "tool": "search_orders", | |
| "arguments": {"product": "Laptop"} | |
| } | |
| env.step(action) | |
| summary = env.step_summaries[0] | |
| assert "Step" in summary | |
| assert "search_orders" in summary | |
| assert "results" in summary | |
| def test_multiple_summaries_preserved(self): | |
| """Test that multiple step summaries are preserved.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| actions = [ | |
| {"tool": "search_customers", "arguments": {"tier": "Gold"}}, | |
| {"tool": "search_tickets", "arguments": {"priority": "High"}}, | |
| {"tool": "search_orders", "arguments": {"status": "Completed"}}, | |
| ] | |
| for action in actions: | |
| env.step(action) | |
| assert len(env.step_summaries) == 3 | |
| assert "search_customers" in env.step_summaries[0] | |
| assert "search_tickets" in env.step_summaries[1] | |
| assert "search_orders" in env.step_summaries[2] | |
| class TestMemoryReuseRewards: | |
| """Test memory reuse reward components.""" | |
| def test_memory_reuse_bonus(self): | |
| """Test that memory reuse is tracked and rewarded.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| # First query | |
| action1 = { | |
| "tool": "search_customers", | |
| "arguments": {"tier": "Gold"} | |
| } | |
| obs1, reward1, done1, info1 = env.step(action1) | |
| # Second query (different filter) | |
| action2 = { | |
| "tool": "search_customers", | |
| "arguments": {"tier": "Silver"} | |
| } | |
| obs2, reward2, done2, info2 = env.step(action2) | |
| # Both should have valid rewards | |
| assert reward1.value > -10 | |
| assert reward2.value > -10 | |
| def test_cache_maintained_component(self): | |
| """Test cache maintained reward component.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| # Multiple searches build cache | |
| action1 = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| obs1, reward1, done1, info1 = env.step(action1) | |
| action2 = {"tool": "search_tickets", "arguments": {"priority": "High"}} | |
| obs2, reward2, done2, info2 = env.step(action2) | |
| # Verify cache is growing | |
| assert len(env.retrieved_entities['customers']) > 0 | |
| assert len(env.retrieved_entities['tickets']) > 0 | |
| def test_memory_hit_tracking(self): | |
| """Test that memory hits are tracked in history.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| env.step(action) | |
| # Check history tracks memory hit | |
| assert len(env.history) == 1 | |
| assert "memory_hit" in env.history[0] | |
| class TestRedundantQueryPenalties: | |
| """Test penalties for redundant queries.""" | |
| def test_repeated_query_penalty(self): | |
| """Test that repeated queries receive penalties.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = { | |
| "tool": "search_customers", | |
| "arguments": {"tier": "Gold"} | |
| } | |
| # First query | |
| obs1, reward1, done1, info1 = env.step(action) | |
| base_reward = reward1.value | |
| # Repeated query | |
| obs2, reward2, done2, info2 = env.step(action) | |
| # Repeated query should have penalty | |
| assert "repeated_query" in reward2.components | |
| assert reward2.components["repeated_query"] < 0 | |
| def test_different_queries_no_penalty(self): | |
| """Test that different queries don't trigger repeated penalty.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action1 = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| action2 = {"tool": "search_customers", "arguments": {"tier": "Silver"}} | |
| obs1, reward1, done1, info1 = env.step(action1) | |
| obs2, reward2, done2, info2 = env.step(action2) | |
| # Second query is different, no repeated_query penalty | |
| assert "repeated_query" not in reward2.components or reward2.components.get("repeated_query") >= 0 | |
| class TestMemoryResetOnEpisode: | |
| """Test that memory resets properly between episodes.""" | |
| def test_memory_reset_on_new_episode(self): | |
| """Test that memory clears on environment reset.""" | |
| env = CRMQueryEnv() | |
| # First episode | |
| env.reset() | |
| action = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| env.step(action) | |
| entities_after_step = len(env.retrieved_entities['customers']) | |
| assert entities_after_step > 0 | |
| # Reset environment | |
| env.reset() | |
| # Memory should be cleared | |
| assert len(env.retrieved_entities['customers']) == 0 | |
| assert len(env.retrieved_entities['orders']) == 0 | |
| assert len(env.retrieved_entities['tickets']) == 0 | |
| assert len(env.step_summaries) == 0 | |
| def test_query_history_reset(self): | |
| """Test that query history resets on environment reset.""" | |
| env = CRMQueryEnv() | |
| env.reset() | |
| action = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| obs1, reward1, done1, info1 = env.step(action) | |
| # First repeat has penalty | |
| obs2, reward2, done2, info2 = env.step(action) | |
| assert reward2.components.get("repeated_query", 0) < 0 | |
| # Reset | |
| env.reset() | |
| # After reset, query should not be in history | |
| obs3, reward3, done3, info3 = env.step(action) | |
| # No penalty for first occurrence in new episode | |
| assert reward3.components.get("repeated_query", 0) >= 0 | |
| class TestMemoryObservation: | |
| """Test memory information in observations.""" | |
| def test_observation_includes_memory_cache(self): | |
| """Test that observations include memory cache.""" | |
| env = CRMQueryEnv() | |
| obs = env.reset() | |
| assert hasattr(obs, 'memory_cache') | |
| assert isinstance(obs.memory_cache, dict) | |
| def test_observation_includes_step_summaries(self): | |
| """Test that observations include step summaries.""" | |
| env = CRMQueryEnv() | |
| obs = env.reset() | |
| assert hasattr(obs, 'step_summaries') | |
| assert isinstance(obs.step_summaries, list) | |
| def test_memory_info_updated_in_observation(self): | |
| """Test that memory info is updated in subsequent observations.""" | |
| env = CRMQueryEnv() | |
| obs1 = env.reset() | |
| initial_summaries = len(obs1.step_summaries) | |
| action = {"tool": "search_customers", "arguments": {"tier": "Gold"}} | |
| obs2, reward, done, info = env.step(action) | |
| assert len(obs2.step_summaries) > initial_summaries | |
| assert len(obs2.memory_cache['customers']) > 0 | |