feat(agent): complete EvoAgent integration for all 6 agent roles
Migrate all agent roles from Legacy to EvoAgent architecture: - fundamentals_analyst, technical_analyst, sentiment_analyst, valuation_analyst - risk_manager, portfolio_manager Key changes: - EvoAgent now supports Portfolio Manager compatibility methods (_make_decision, get_decisions, get_portfolio_state, load_portfolio_state, update_portfolio) - Add UnifiedAgentFactory for centralized agent creation - ToolGuard with batch approval API and WebSocket broadcast - Legacy agents marked deprecated (AnalystAgent, RiskAgent, PMAgent) - Remove backend/agents/compat.py migration shim - Add run_id alongside workspace_id for semantic clarity - Complete integration test coverage (13 tests) - All smoke tests passing for 6 agent roles Constraint: Must maintain backward compatibility with existing run configs Constraint: Memory support must work with EvoAgent (no fallback to Legacy) Rejected: Separate PM implementation for EvoAgent | unified approach cleaner Confidence: high Scope-risk: broad Directive: EVO_AGENT_IDS env var still respected but defaults to all roles Not-tested: Kubernetes sandbox mode for skill execution
This commit is contained in:
@@ -200,6 +200,179 @@ def test_trading_service_market_cap_endpoint(monkeypatch):
|
||||
}
|
||||
|
||||
|
||||
def test_trading_service_contract_stability():
|
||||
"""Verify trading service API maintains contract stability."""
|
||||
app = create_app()
|
||||
routes = {route.path: route for route in app.routes if hasattr(route, "methods")}
|
||||
|
||||
# Health endpoint
|
||||
assert "/health" in routes
|
||||
|
||||
# Trading data endpoints
|
||||
assert "/api/prices" in routes
|
||||
assert "/api/financials" in routes
|
||||
assert "/api/news" in routes
|
||||
assert "/api/insider-trades" in routes
|
||||
assert "/api/market/status" in routes
|
||||
assert "/api/market-cap" in routes
|
||||
assert "/api/line-items" in routes
|
||||
|
||||
# Verify all are GET endpoints (read-only service)
|
||||
for path in ["/api/prices", "/api/financials", "/api/news", "/api/insider-trades",
|
||||
"/api/market/status", "/api/market-cap", "/api/line-items"]:
|
||||
assert "GET" in routes[path].methods
|
||||
|
||||
|
||||
def test_trading_service_prices_contract(monkeypatch):
|
||||
"""Test prices endpoint maintains response contract."""
|
||||
monkeypatch.setattr(
|
||||
"backend.domains.trading.get_prices_payload",
|
||||
lambda ticker, start_date, end_date: {
|
||||
"ticker": ticker,
|
||||
"prices": [
|
||||
Price(
|
||||
open=1.0,
|
||||
close=2.0,
|
||||
high=2.5,
|
||||
low=0.5,
|
||||
volume=100,
|
||||
time="2026-03-20",
|
||||
)
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
with TestClient(create_app()) as client:
|
||||
response = client.get(
|
||||
"/api/prices",
|
||||
params={
|
||||
"ticker": "AAPL",
|
||||
"start_date": "2026-03-01",
|
||||
"end_date": "2026-03-20",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert "ticker" in payload
|
||||
assert "prices" in payload
|
||||
assert isinstance(payload["prices"], list)
|
||||
if payload["prices"]:
|
||||
price = payload["prices"][0]
|
||||
assert "open" in price
|
||||
assert "close" in price
|
||||
assert "high" in price
|
||||
assert "low" in price
|
||||
assert "volume" in price
|
||||
assert "time" in price
|
||||
|
||||
|
||||
def test_trading_service_financials_contract(monkeypatch):
|
||||
"""Test financials endpoint maintains response contract."""
|
||||
monkeypatch.setattr(
|
||||
"backend.domains.trading.get_financials_payload",
|
||||
lambda ticker, end_date, period, limit: {
|
||||
"financial_metrics": [
|
||||
FinancialMetrics(
|
||||
ticker=ticker,
|
||||
report_period=end_date,
|
||||
period=period,
|
||||
currency="USD",
|
||||
market_cap=123.0,
|
||||
enterprise_value=None,
|
||||
price_to_earnings_ratio=None,
|
||||
price_to_book_ratio=None,
|
||||
price_to_sales_ratio=None,
|
||||
enterprise_value_to_ebitda_ratio=None,
|
||||
enterprise_value_to_revenue_ratio=None,
|
||||
free_cash_flow_yield=None,
|
||||
peg_ratio=None,
|
||||
gross_margin=None,
|
||||
operating_margin=None,
|
||||
net_margin=None,
|
||||
return_on_equity=None,
|
||||
return_on_assets=None,
|
||||
return_on_invested_capital=None,
|
||||
asset_turnover=None,
|
||||
inventory_turnover=None,
|
||||
receivables_turnover=None,
|
||||
days_sales_outstanding=None,
|
||||
operating_cycle=None,
|
||||
working_capital_turnover=None,
|
||||
current_ratio=None,
|
||||
quick_ratio=None,
|
||||
cash_ratio=None,
|
||||
operating_cash_flow_ratio=None,
|
||||
debt_to_equity=None,
|
||||
debt_to_assets=None,
|
||||
interest_coverage=None,
|
||||
revenue_growth=None,
|
||||
earnings_growth=None,
|
||||
book_value_growth=None,
|
||||
earnings_per_share_growth=None,
|
||||
free_cash_flow_growth=None,
|
||||
operating_income_growth=None,
|
||||
ebitda_growth=None,
|
||||
payout_ratio=None,
|
||||
earnings_per_share=None,
|
||||
book_value_per_share=None,
|
||||
free_cash_flow_per_share=None,
|
||||
)
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
with TestClient(create_app()) as client:
|
||||
response = client.get(
|
||||
"/api/financials",
|
||||
params={"ticker": "AAPL", "end_date": "2026-03-20"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert "financial_metrics" in payload
|
||||
assert isinstance(payload["financial_metrics"], list)
|
||||
|
||||
|
||||
def test_trading_service_market_status_contract(monkeypatch):
|
||||
"""Test market status endpoint maintains response contract."""
|
||||
monkeypatch.setattr(
|
||||
"backend.domains.trading.get_market_status_payload",
|
||||
lambda: {"status": "open", "status_text": "Open", "next_open": "09:30"},
|
||||
)
|
||||
|
||||
with TestClient(create_app()) as client:
|
||||
response = client.get("/api/market/status")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert "status" in payload
|
||||
|
||||
|
||||
def test_trading_service_market_cap_contract(monkeypatch):
|
||||
"""Test market cap endpoint maintains response contract."""
|
||||
monkeypatch.setattr(
|
||||
"backend.domains.trading.get_market_cap_payload",
|
||||
lambda ticker, end_date: {
|
||||
"ticker": ticker,
|
||||
"end_date": end_date,
|
||||
"market_cap": 3.5e12,
|
||||
},
|
||||
)
|
||||
|
||||
with TestClient(create_app()) as client:
|
||||
response = client.get(
|
||||
"/api/market-cap",
|
||||
params={"ticker": "AAPL", "end_date": "2026-03-20"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert "ticker" in payload
|
||||
assert "end_date" in payload
|
||||
assert "market_cap" in payload
|
||||
|
||||
|
||||
def test_trading_service_line_items_endpoint(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"backend.domains.trading.get_line_items_payload",
|
||||
|
||||
Reference in New Issue
Block a user