From 83f1edcd12ff305d912e77003b50908f6d0d7e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=8A?= Date: Mon, 2 Mar 2026 10:25:01 +0530 Subject: [PATCH] Add strategy architect agent to daily planning committee --- .../intelligence/agents/agent_orchestrator.py | 13 +++- backend/services/today_workflow_service.py | 2 +- .../test_today_workflow_pillar_coverage.py | 71 +++++++++++++++++-- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/backend/services/intelligence/agents/agent_orchestrator.py b/backend/services/intelligence/agents/agent_orchestrator.py index 161a7ed1..09adfd74 100644 --- a/backend/services/intelligence/agents/agent_orchestrator.py +++ b/backend/services/intelligence/agents/agent_orchestrator.py @@ -24,7 +24,11 @@ from services.intelligence.agents.core_agent_framework import ( BaseALwrityAgent, AgentAction, AgentPerformance, StrategyOrchestratorAgent ) from services.intelligence.agents.specialized_agents import ( - ContentStrategyAgent, CompetitorResponseAgent, SEOOptimizationAgent, SocialAmplificationAgent + ContentStrategyAgent, + CompetitorResponseAgent, + SEOOptimizationAgent, + SocialAmplificationAgent, + StrategyArchitectAgent, ) from services.intelligence.agents.trend_surfer_agent import TrendSurferAgent from services.intelligence.agents.market_signal_detector import ( @@ -141,6 +145,13 @@ class ALwrityAgentOrchestrator: if enabled_by_key.get("content_strategist", True): self.content_agent = ContentStrategyAgent(self.user_id, self.config.shared_llm, llm=self.llm) self.agents['content'] = self.content_agent + + # Strategy Architect Agent + if enabled_by_key.get("strategy_architect", True): + from services.intelligence.txtai_service import TxtaiIntelligenceService + intel_service = TxtaiIntelligenceService(self.user_id) + self.strategy_agent = StrategyArchitectAgent(intel_service, self.user_id) + self.agents['strategy'] = self.strategy_agent # Competitor Response Agent if enabled_by_key.get("competitor_analyst", True): diff --git a/backend/services/today_workflow_service.py b/backend/services/today_workflow_service.py index 0aef12cb..5b1ca693 100644 --- a/backend/services/today_workflow_service.py +++ b/backend/services/today_workflow_service.py @@ -279,10 +279,10 @@ async def generate_agent_enhanced_plan(db: Session, user_id: str, date: str) -> # Define agents to poll agents_to_poll = [ orchestrator.agents.get('content'), # ContentStrategyAgent + orchestrator.agents.get('strategy'), # StrategyArchitectAgent orchestrator.agents.get('seo'), # SEOOptimizationAgent orchestrator.agents.get('social'), # SocialAmplificationAgent orchestrator.agents.get('competitor'), # CompetitorResponseAgent - # Add StrategyArchitect if available in orchestrator.agents ] # Filter out None agents (disabled/failed init) diff --git a/backend/tests/test_today_workflow_pillar_coverage.py b/backend/tests/test_today_workflow_pillar_coverage.py index e8fe3838..9bb9a821 100644 --- a/backend/tests/test_today_workflow_pillar_coverage.py +++ b/backend/tests/test_today_workflow_pillar_coverage.py @@ -20,6 +20,14 @@ class DummyActivity: return None +class DummyMemoryService: + def __init__(self, user_id, db): + pass + + async def filter_redundant_proposals(self, proposals): + return proposals + + class DummyAgent: def __init__(self, proposals): self._proposals = proposals @@ -28,10 +36,11 @@ class DummyAgent: return self._proposals -def _mock_orchestrator_with_agents(proposals): +def _mock_orchestrator_with_agents(content_proposals=None, strategy_proposals=None): return SimpleNamespace( agents={ - "content": DummyAgent(proposals), + "content": DummyAgent(content_proposals or []), + "strategy": DummyAgent(strategy_proposals or []), "seo": None, "social": None, "competitor": None, @@ -55,7 +64,7 @@ async def test_generate_agent_enhanced_plan_preserves_full_committee_coverage(mo ] async def _get_orchestrator(user_id): - return _mock_orchestrator_with_agents(proposals) + return _mock_orchestrator_with_agents(content_proposals=proposals) monkeypatch.setattr(svc, "build_grounding_context", lambda db, user_id, date: {}) monkeypatch.setattr(svc.orchestration_service, "get_or_create_orchestrator", _get_orchestrator) @@ -74,7 +83,7 @@ async def test_generate_agent_enhanced_plan_backfills_missing_committee_pillars( ] async def _get_orchestrator(user_id): - return _mock_orchestrator_with_agents(proposals) + return _mock_orchestrator_with_agents(content_proposals=proposals) monkeypatch.setattr(svc, "build_grounding_context", lambda db, user_id, date: {}) monkeypatch.setattr(svc.orchestration_service, "get_or_create_orchestrator", _get_orchestrator) @@ -88,7 +97,7 @@ async def test_generate_agent_enhanced_plan_backfills_missing_committee_pillars( @pytest.mark.asyncio async def test_generate_agent_enhanced_plan_full_fallback_path_still_covers_all_pillars(monkeypatch): async def _get_orchestrator(user_id): - return _mock_orchestrator_with_agents([]) + return _mock_orchestrator_with_agents() monkeypatch.setattr(svc, "build_grounding_context", lambda db, user_id, date: {}) monkeypatch.setattr(svc.orchestration_service, "get_or_create_orchestrator", _get_orchestrator) @@ -103,3 +112,55 @@ async def test_generate_agent_enhanced_plan_full_fallback_path_still_covers_all_ assert _covered_pillars(result) == set(svc.PILLAR_IDS) assert len(result["tasks"]) >= len(svc.PILLAR_IDS) + + +@pytest.mark.asyncio +async def test_generate_agent_enhanced_plan_strategy_plan_task_survives_dedupe_and_coverage(monkeypatch): + content_proposals = [ + TaskProposal( + "Review Strategic Goals", + "desc", + "plan", + "medium", + 10, + "ContentStrategyAgent", + "why", + {}, + "navigate", + "/content-planning-dashboard", + ), + ] + strategy_proposals = [ + TaskProposal( + "Review Strategic Goals", + "desc", + "plan", + "high", + 10, + "StrategyArchitectAgent", + "why", + {}, + "navigate", + "/content-planning-dashboard", + ), + ] + + async def _get_orchestrator(user_id): + return _mock_orchestrator_with_agents( + content_proposals=content_proposals, + strategy_proposals=strategy_proposals, + ) + + monkeypatch.setattr(svc, "build_grounding_context", lambda db, user_id, date: {}) + monkeypatch.setattr(svc.orchestration_service, "get_or_create_orchestrator", _get_orchestrator) + monkeypatch.setattr(svc, "TaskMemoryService", DummyMemoryService) + + result = await svc.generate_agent_enhanced_plan(db=None, user_id="u1", date="2026-01-01") + + assert _covered_pillars(result) == set(svc.PILLAR_IDS) + plan_tasks = [task for task in result["tasks"] if task["pillarId"] == "plan"] + assert any( + task["title"] == "Review Strategic Goals" + and task["metadata"].get("source_agent") == "StrategyArchitectAgent" + for task in plan_tasks + )