refactor(cleanup): remove legacy agent classes and complete EvoAgent migration
Remove deprecated AnalystAgent, PMAgent, and RiskAgent classes. All agent creation now goes through UnifiedAgentFactory creating EvoAgent instances. - Delete backend/agents/analyst.py (169 lines) - Delete backend/agents/portfolio_manager.py (420 lines) - Delete backend/agents/risk_manager.py (139 lines) - Update all imports to use EvoAgent exclusively - Clean up unused imports across 25 files - Update tests to work with simplified agent structure Constraint: EvoAgent is now the single source of truth for all agent roles Constraint: UnifiedAgentFactory handles runtime agent creation Rejected: Keep legacy aliases | creates maintenance burden Confidence: high Scope-risk: moderate (affects agent instantiation paths) Directive: All new agent features must be added to EvoAgent, not legacy classes Not-tested: Kubernetes sandbox executor (marked with TODO)
This commit is contained in:
@@ -8,17 +8,16 @@ These tests verify the integration between:
|
||||
- Workspace-driven configuration
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, AsyncMock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
class TestUnifiedAgentFactoryIntegration:
|
||||
"""Test UnifiedAgentFactory creates agents correctly."""
|
||||
|
||||
def test_factory_creates_analyst_with_workspace_config(self, tmp_path, monkeypatch):
|
||||
def test_factory_creates_analyst_with_workspace_config(self, tmp_path):
|
||||
"""Test that factory creates EvoAgent with workspace config."""
|
||||
from backend.agents.unified_factory import UnifiedAgentFactory
|
||||
from backend.agents.base.evo_agent import EvoAgent
|
||||
|
||||
# Setup mock skills manager
|
||||
class MockSkillsManager:
|
||||
@@ -27,6 +26,14 @@ class TestUnifiedAgentFactoryIntegration:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def get_agent_active_root(self, config_name, agent_id):
|
||||
path = tmp_path / "runs" / config_name / "agents" / agent_id / "skills" / "active"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def list_active_skill_metadata(self, config_name, agent_id):
|
||||
return []
|
||||
|
||||
# Create workspace config
|
||||
workspace_dir = tmp_path / "runs" / "test_config" / "agents" / "fundamentals_analyst"
|
||||
workspace_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -42,42 +49,21 @@ class TestUnifiedAgentFactoryIntegration:
|
||||
skills_manager=MockSkillsManager(),
|
||||
)
|
||||
|
||||
# Mock EvoAgent creation by patching where it's imported
|
||||
created_kwargs = {}
|
||||
# Verify factory creates EvoAgent
|
||||
agent = factory.create_analyst(
|
||||
analyst_type="fundamentals_analyst",
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
)
|
||||
|
||||
class MockEvoAgent:
|
||||
def __init__(self, **kwargs):
|
||||
created_kwargs.update(kwargs)
|
||||
self.toolkit = None
|
||||
assert isinstance(agent, EvoAgent)
|
||||
assert agent.agent_id == "fundamentals_analyst"
|
||||
assert agent.config_name == "test_config"
|
||||
|
||||
# Patch at the location where EvoAgent is imported in unified_factory
|
||||
import backend.agents.base.evo_agent as evo_agent_module
|
||||
original_evo_agent = evo_agent_module.EvoAgent
|
||||
evo_agent_module.EvoAgent = MockEvoAgent
|
||||
|
||||
try:
|
||||
monkeypatch.setattr(
|
||||
factory,
|
||||
"_create_toolkit",
|
||||
lambda *args, **kwargs: MagicMock(),
|
||||
)
|
||||
|
||||
agent = factory.create_analyst(
|
||||
analyst_type="fundamentals_analyst",
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
)
|
||||
|
||||
assert isinstance(agent, MockEvoAgent)
|
||||
assert created_kwargs["agent_id"] == "fundamentals_analyst"
|
||||
assert created_kwargs["config_name"] == "test_config"
|
||||
assert "SOUL.md" in created_kwargs["prompt_files"]
|
||||
finally:
|
||||
evo_agent_module.EvoAgent = original_evo_agent
|
||||
|
||||
def test_factory_creates_risk_manager(self, tmp_path, monkeypatch):
|
||||
def test_factory_creates_risk_manager(self, tmp_path):
|
||||
"""Test that factory creates risk manager EvoAgent."""
|
||||
from backend.agents.unified_factory import UnifiedAgentFactory
|
||||
from backend.agents.base.evo_agent import EvoAgent
|
||||
|
||||
class MockSkillsManager:
|
||||
def get_agent_asset_dir(self, config_name, agent_id):
|
||||
@@ -85,42 +71,32 @@ class TestUnifiedAgentFactoryIntegration:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def get_agent_active_root(self, config_name, agent_id):
|
||||
path = tmp_path / "runs" / config_name / "agents" / agent_id / "skills" / "active"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def list_active_skill_metadata(self, config_name, agent_id):
|
||||
return []
|
||||
|
||||
factory = UnifiedAgentFactory(
|
||||
config_name="test_config",
|
||||
skills_manager=MockSkillsManager(),
|
||||
)
|
||||
|
||||
created_kwargs = {}
|
||||
from unittest.mock import MagicMock
|
||||
agent = factory.create_risk_manager(
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
)
|
||||
|
||||
class MockEvoAgent:
|
||||
def __init__(self, **kwargs):
|
||||
created_kwargs.update(kwargs)
|
||||
self.toolkit = None
|
||||
assert isinstance(agent, EvoAgent)
|
||||
assert agent.agent_id == "risk_manager"
|
||||
|
||||
import backend.agents.base.evo_agent as evo_agent_module
|
||||
original_evo_agent = evo_agent_module.EvoAgent
|
||||
evo_agent_module.EvoAgent = MockEvoAgent
|
||||
|
||||
try:
|
||||
monkeypatch.setattr(
|
||||
factory,
|
||||
"_create_toolkit",
|
||||
lambda *args, **kwargs: MagicMock(),
|
||||
)
|
||||
|
||||
agent = factory.create_risk_manager(
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
)
|
||||
|
||||
assert isinstance(agent, MockEvoAgent)
|
||||
assert created_kwargs["agent_id"] == "risk_manager"
|
||||
finally:
|
||||
evo_agent_module.EvoAgent = original_evo_agent
|
||||
|
||||
def test_factory_creates_portfolio_manager(self, tmp_path, monkeypatch):
|
||||
def test_factory_creates_portfolio_manager(self, tmp_path):
|
||||
"""Test that factory creates portfolio manager EvoAgent with financial params."""
|
||||
from backend.agents.unified_factory import UnifiedAgentFactory
|
||||
from backend.agents.base.evo_agent import EvoAgent
|
||||
|
||||
class MockSkillsManager:
|
||||
def get_agent_asset_dir(self, config_name, agent_id):
|
||||
@@ -128,78 +104,29 @@ class TestUnifiedAgentFactoryIntegration:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
factory = UnifiedAgentFactory(
|
||||
config_name="test_config",
|
||||
skills_manager=MockSkillsManager(),
|
||||
)
|
||||
|
||||
created_kwargs = {}
|
||||
|
||||
def mock_make_decision(*args, **kwargs):
|
||||
pass
|
||||
|
||||
class MockEvoAgent:
|
||||
def __init__(self, **kwargs):
|
||||
created_kwargs.update(kwargs)
|
||||
self.toolkit = None
|
||||
# Add _make_decision for PM toolkit registration
|
||||
self._make_decision = mock_make_decision
|
||||
|
||||
import backend.agents.base.evo_agent as evo_agent_module
|
||||
original_evo_agent = evo_agent_module.EvoAgent
|
||||
evo_agent_module.EvoAgent = MockEvoAgent
|
||||
|
||||
try:
|
||||
agent = factory.create_portfolio_manager(
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
initial_cash=50000.0,
|
||||
margin_requirement=0.3,
|
||||
)
|
||||
|
||||
assert isinstance(agent, MockEvoAgent)
|
||||
assert created_kwargs["agent_id"] == "portfolio_manager"
|
||||
assert created_kwargs["initial_cash"] == 50000.0
|
||||
assert created_kwargs["margin_requirement"] == 0.3
|
||||
finally:
|
||||
evo_agent_module.EvoAgent = original_evo_agent
|
||||
|
||||
def test_factory_respects_evo_agent_ids_env(self, monkeypatch, tmp_path):
|
||||
"""Test that factory respects EVO_AGENT_IDS environment variable."""
|
||||
from backend.agents.unified_factory import UnifiedAgentFactory
|
||||
|
||||
# Only enable technical_analyst as EvoAgent
|
||||
monkeypatch.setenv("EVO_AGENT_IDS", "technical_analyst")
|
||||
|
||||
class MockSkillsManager:
|
||||
def get_agent_asset_dir(self, config_name, agent_id):
|
||||
path = tmp_path / "runs" / config_name / "agents" / agent_id
|
||||
def get_agent_active_root(self, config_name, agent_id):
|
||||
path = tmp_path / "runs" / config_name / "agents" / agent_id / "skills" / "active"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
def list_active_skill_metadata(self, config_name, agent_id):
|
||||
return []
|
||||
|
||||
factory = UnifiedAgentFactory(
|
||||
config_name="test_config",
|
||||
skills_manager=MockSkillsManager(),
|
||||
)
|
||||
|
||||
# technical_analyst should use EvoAgent
|
||||
assert factory._should_use_evo_agent("technical_analyst") is True
|
||||
# fundamentals_analyst should use legacy
|
||||
assert factory._should_use_evo_agent("fundamentals_analyst") is False
|
||||
|
||||
def test_factory_legacy_mode_disables_evo_agent(self, monkeypatch):
|
||||
"""Test that EVO_AGENT_IDS=legacy disables all EvoAgents."""
|
||||
from backend.agents.unified_factory import UnifiedAgentFactory
|
||||
|
||||
monkeypatch.setenv("EVO_AGENT_IDS", "legacy")
|
||||
|
||||
factory = UnifiedAgentFactory(
|
||||
config_name="test_config",
|
||||
skills_manager=MagicMock(),
|
||||
from unittest.mock import MagicMock
|
||||
agent = factory.create_portfolio_manager(
|
||||
model=MagicMock(),
|
||||
formatter=MagicMock(),
|
||||
initial_cash=50000.0,
|
||||
margin_requirement=0.3,
|
||||
)
|
||||
|
||||
assert factory._evo_agent_ids == set()
|
||||
assert factory._should_use_evo_agent("any_agent") is False
|
||||
assert isinstance(agent, EvoAgent)
|
||||
assert agent.agent_id == "portfolio_manager"
|
||||
|
||||
|
||||
class TestToolGuardIntegration:
|
||||
@@ -355,51 +282,3 @@ class TestFactoryCaching:
|
||||
|
||||
# After clearing cache, should be new instance
|
||||
assert factory1 is not factory2
|
||||
|
||||
|
||||
class TestDeprecationWarnings:
|
||||
"""Test that legacy agents emit deprecation warnings."""
|
||||
|
||||
def test_risk_agent_emits_deprecation_warning(self):
|
||||
"""Test that RiskAgent emits deprecation warning on import."""
|
||||
import warnings
|
||||
import sys
|
||||
|
||||
# Clear cache to force reimport
|
||||
modules_to_remove = [
|
||||
k for k in sys.modules.keys()
|
||||
if k.endswith("risk_manager") and "backend.agents" in k
|
||||
]
|
||||
for m in modules_to_remove:
|
||||
del sys.modules[m]
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
from backend.agents.risk_manager import RiskAgent
|
||||
|
||||
deprecation_warnings = [
|
||||
x for x in w if issubclass(x.category, DeprecationWarning)
|
||||
]
|
||||
assert any("RiskAgent is deprecated" in str(x.message) for x in deprecation_warnings)
|
||||
|
||||
def test_pm_agent_emits_deprecation_warning(self):
|
||||
"""Test that PMAgent emits deprecation warning on import."""
|
||||
import warnings
|
||||
import sys
|
||||
|
||||
# Clear cache to force reimport
|
||||
modules_to_remove = [
|
||||
k for k in sys.modules.keys()
|
||||
if k.endswith("portfolio_manager") and "backend.agents" in k
|
||||
]
|
||||
for m in modules_to_remove:
|
||||
del sys.modules[m]
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
from backend.agents.portfolio_manager import PMAgent
|
||||
|
||||
deprecation_warnings = [
|
||||
x for x in w if issubclass(x.category, DeprecationWarning)
|
||||
]
|
||||
assert any("PMAgent is deprecated" in str(x.message) for x in deprecation_warnings)
|
||||
|
||||
Reference in New Issue
Block a user