""" Core Agent Framework for ALwrity Autonomous Marketing System Built on txtai's native Agent framework (smolagents) """ import asyncio import json import logging from datetime import datetime from typing import Dict, List, Any, Optional, Callable from dataclasses import dataclass, asdict from abc import ABC, abstractmethod # txtai imports for native agent framework try: from txtai import Agent, LLM TXTAI_AVAILABLE = Agent.__module__ != "txtai.agent.placeholder" except ImportError: TXTAI_AVAILABLE = False # Fallback implementation for development logging.warning("txtai not available, using fallback implementation") # Optional MLflow integration try: import mlflow MLFLOW_AVAILABLE = True except ImportError: MLFLOW_AVAILABLE = False from utils.logger_utils import get_service_logger from services.database import get_session_for_user from services.intelligence.monitoring.semantic_dashboard import RealTimeSemanticMonitor from services.intelligence.agents.safety_framework import get_safety_framework from services.agent_activity_service import AgentActivityService logger = get_service_logger(__name__) @dataclass class AgentAction: """Represents an action taken by an agent""" action_id: str agent_type: str action_type: str target_resource: str parameters: Dict[str, Any] expected_outcome: str risk_level: float # 0.0 to 1.0 requires_approval: bool = False created_at: str = None def __post_init__(self): if self.created_at is None: self.created_at = datetime.utcnow().isoformat() @dataclass class MarketSignal: """Represents a market change or opportunity""" signal_id: str signal_type: str # 'competitor', 'serp', 'social', 'industry', 'performance' source: str description: str impact_score: float # 0.0 to 1.0 urgency_level: str # 'low', 'medium', 'high', 'critical' confidence_score: float # 0.0 to 1.0 related_topics: List[str] suggested_actions: List[str] detected_at: str = None expires_at: str = None def __post_init__(self): if self.detected_at is None: self.detected_at = datetime.utcnow().isoformat() if self.expires_at is None: # Default expiration: 7 days for most signals expires = datetime.utcnow().timestamp() + (7 * 24 * 60 * 60) self.expires_at = datetime.fromtimestamp(expires).isoformat() @dataclass class AgentPerformance: """Performance metrics for an agent""" agent_id: str total_actions: int successful_actions: int failed_actions: int average_response_time: float success_rate: float last_action_at: str efficiency_score: float # 0.0 to 1.0 class BaseALwrityAgent(ABC): """Base class for all ALwrity marketing agents""" _prompt_context_cache: Dict[str, Dict[str, Any]] = {} _profile_cache: Dict[str, Dict[str, Any]] = {} def __init__(self, user_id: str, agent_type: str, model_name: str = "Qwen/Qwen3-4B-Instruct-2507", llm: Any = None, enable_tracing: bool = True): self.user_id = user_id self.agent_type = agent_type self.model_name = model_name self.agent_id = f"{agent_type}_{user_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}" self.enable_tracing = enable_tracing self.performance = AgentPerformance( agent_id=self.agent_id, total_actions=0, successful_actions=0, failed_actions=0, average_response_time=0.0, success_rate=0.0, last_action_at=None, efficiency_score=0.0 ) # Initialize txtai agent if available self.txtai_agent = None self.llm = llm # Ensure llm is set if provided, regardless of txtai availability self.agent_key = self._resolve_agent_key(agent_type) self._agent_profile = self._load_agent_profile_overrides() self._prompt_context = self._load_prompt_context() if TXTAI_AVAILABLE: try: if not self.llm: self.llm = LLM(model_name) self.txtai_agent = self._create_txtai_agent() logger.info(f"Initialized txtai agent for {agent_type} - {self.agent_id}") except Exception as e: logger.error(f"Failed to initialize txtai agent for {agent_type}: {e}") self.txtai_agent = self._create_fallback_agent() else: self.txtai_agent = self._create_fallback_agent() # Initialize safety framework self.safety_framework = get_safety_framework(user_id) def _resolve_agent_key(self, agent_type: str) -> str: value = str(agent_type or "").strip() if value.lower() == "strategyorchestrator".lower(): return "strategy_orchestrator" return value def _load_agent_profile_overrides(self) -> Dict[str, Any]: cache_key = f"{self.user_id}:{self.agent_key}" cached = BaseALwrityAgent._profile_cache.get(cache_key) if cached is not None: return cached profile_data: Dict[str, Any] = {} db = None try: db = get_session_for_user(self.user_id) if not db: BaseALwrityAgent._profile_cache[cache_key] = profile_data return profile_data from models.agent_activity_models import AgentProfile profile = ( db.query(AgentProfile) .filter(AgentProfile.user_id == self.user_id, AgentProfile.agent_key == self.agent_key) .first() ) if not profile: profile = ( db.query(AgentProfile) .filter(AgentProfile.user_id == self.user_id, AgentProfile.agent_type == self.agent_type) .first() ) if profile: profile_data = { "display_name": profile.display_name, "enabled": bool(profile.enabled) if profile.enabled is not None else None, "schedule": profile.schedule, "notification_prefs": profile.notification_prefs, "tone": profile.tone, "system_prompt": profile.system_prompt, "task_prompt_template": profile.task_prompt_template, "reporting_prefs": profile.reporting_prefs, } except Exception: profile_data = {} finally: try: if db: db.close() except Exception: pass BaseALwrityAgent._profile_cache[cache_key] = profile_data return profile_data def _load_prompt_context(self) -> Dict[str, Any]: cached = BaseALwrityAgent._prompt_context_cache.get(self.user_id) if cached is not None: return cached context: Dict[str, Any] = {"website_name": "Your", "website_url": "", "user_id": self.user_id} db = None try: db = get_session_for_user(self.user_id) if not db: BaseALwrityAgent._prompt_context_cache[self.user_id] = context return context from api.content_planning.services.content_strategy.onboarding.data_integration import ( OnboardingDataIntegrationService, ) svc = OnboardingDataIntegrationService() integrated = svc.get_integrated_data_sync(self.user_id, db) or {} website_analysis = integrated.get("website_analysis") or {} canonical = integrated.get("canonical_profile") or {} website_url = ( website_analysis.get("website_url") or website_analysis.get("website") or canonical.get("website_url") or canonical.get("website") or "" ) domain = website_analysis.get("domain") or canonical.get("domain") or "" website_name = "" if domain: website_name = str(domain).split(".")[0].strip() if not website_name and website_url: try: from urllib.parse import urlparse host = urlparse(str(website_url)).hostname or "" host = host.replace("www.", "") website_name = host.split(".")[0].strip() or host except Exception: website_name = "" context = { "user_id": self.user_id, "website_url": str(website_url or ""), "website_name": str(website_name or "Your"), } writing_style = canonical.get("writing_style") or {} if isinstance(writing_style, dict): if writing_style.get("tone"): context["writing_tone"] = writing_style.get("tone") if writing_style.get("voice"): context["writing_voice"] = writing_style.get("voice") except Exception: pass finally: try: if db: db.close() except Exception: pass BaseALwrityAgent._prompt_context_cache[self.user_id] = context return context def _render_prompt_template(self, text: str) -> str: value = str(text or "") ctx = self._prompt_context or {} for k, v in ctx.items(): placeholder = "{" + str(k) + "}" if placeholder in value: value = value.replace(placeholder, str(v)) return value def get_effective_system_prompt(self, default_prompt: str) -> str: override = (self._agent_profile or {}).get("system_prompt") selected = override if (override is not None and str(override).strip()) else default_prompt return self._render_prompt_template(selected) def get_effective_task_prompt_template(self, default_template: str = "") -> str: override = (self._agent_profile or {}).get("task_prompt_template") selected = override if (override is not None and str(override).strip()) else default_template return self._render_prompt_template(selected) def build_task_prompt(self, instruction: str, task_context: Optional[Dict[str, Any]] = None, default_template: str = "") -> str: template = self.get_effective_task_prompt_template(default_template or "") context_json = json.dumps(task_context or {}, ensure_ascii=False) if template and template.strip(): return f"{template}\n\nInstruction: {instruction}\nContext: {context_json}" return f"Task: {instruction}\nContext: {context_json}\n\nPlease execute this task using your specialized tools and provide a detailed report." @abstractmethod def _create_txtai_agent(self) -> Agent: """Create txtai agent with specific tools and configuration""" pass def _create_fallback_agent(self): """Fallback agent for development/testing when txtai is not available""" class FallbackAgent: def __init__(self, agent_type: str): self.agent_type = agent_type self.available = False async def run(self, prompt: str, **kwargs) -> str: return f"[FALLBACK] {self.agent_type} agent would process: {prompt[:100]}..." return FallbackAgent(self.agent_type) async def run(self, prompt: str) -> str: """Run the agent with a prompt directly (compatibility method)""" db = None activity = None run_record = None try: try: db = get_session_for_user(self.user_id) if db: activity = AgentActivityService(db, self.user_id) run_record = activity.start_run(agent_type=self.agent_type, prompt=prompt) activity.log_event( event_type="plan", severity="info", message=(prompt[:2000] if prompt else None), payload={"kind": "prompt"}, run_id=run_record.id, agent_type=self.agent_type, ) except Exception: activity = None run_record = None if self.txtai_agent: # Check if txtai_agent has run method (e.g. if it's my fallback agent) if hasattr(self.txtai_agent, 'run'): if asyncio.iscoroutinefunction(self.txtai_agent.run): result = await self.txtai_agent.run(prompt) else: result = self.txtai_agent.run(prompt) else: loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, self.txtai_agent, prompt) if not self.txtai_agent: result = "Agent not initialized" if activity and run_record: activity.log_event( event_type="final_summary", severity="info", message=(str(result)[:2000] if result is not None else None), payload={"kind": "result"}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=True, result_summary=(str(result)[:4000] if result is not None else None)) return result except Exception as e: logger.error(f"Error running agent {self.agent_id}: {e}") if activity and run_record: try: activity.log_event( event_type="error", severity="error", message=str(e)[:2000], payload={"kind": "exception"}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=False, error_message=str(e)[:4000]) activity.create_alert( alert_type="agent_run_failed", title=f"{self.agent_type} failed", message=str(e)[:2000], severity="error", payload={"agent_id": self.agent_id, "agent_type": self.agent_type}, dedupe_key=None, ) except Exception: pass return f"Error: {str(e)}" finally: try: if db: db.close() except Exception: pass async def execute_action(self, action: AgentAction) -> Dict[str, Any]: """Execute an agent action with performance tracking, safety validation, and rollback support""" start_time = datetime.utcnow() checkpoint_id = None db = None activity = None run_record = None try: logger.info(f"Agent {self.agent_id} executing action: {action.action_type}") try: db = get_session_for_user(self.user_id) if db: activity = AgentActivityService(db, self.user_id) run_record = activity.start_run( agent_type=self.agent_type, prompt=f"{action.action_type} -> {action.target_resource}", ) activity.log_event( event_type="plan", severity="info", message=f"{action.action_type} -> {action.target_resource}", payload={"action": asdict(action)}, run_id=run_record.id, agent_type=self.agent_type, ) except Exception: activity = None run_record = None # 1. Validate action safety if not await self._validate_action_safety(action): if activity and run_record: activity.log_event( event_type="decision", severity="warning", message="Action failed safety validation", payload={"action_id": action.action_id, "action_type": action.action_type}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=False, error_message="Action failed safety validation") return { "success": False, "error": "Action failed safety validation", "action_id": action.action_id, "agent_id": self.agent_id } if action.requires_approval: approval_id = None if activity: req = activity.create_approval_request( action_id=action.action_id, action_type=action.action_type, target_resource=action.target_resource, risk_level=action.risk_level, payload=asdict(action), agent_type=self.agent_type, run_id=run_record.id if run_record else None, expires_at=None, ) approval_id = req.id activity.create_alert( alert_type="approval_required", title=f"Approval required: {action.action_type}", message=f"Agent requested approval for {action.action_type} on {action.target_resource}", severity="warning" if action.risk_level < 0.8 else "error", payload={"approval_id": req.id, "action_id": action.action_id, "action_type": action.action_type}, cta_path="/approvals", dedupe_key=f"approval:{req.id}", ) if run_record: activity.log_event( event_type="decision", severity="info", message="Action requires approval", payload={"approval_id": req.id, "action_id": action.action_id}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=False, error_message="Pending approval") return { "success": False, "requires_approval": True, "approval_request_id": approval_id, "action_id": action.action_id, "agent_id": self.agent_id, } # 2. Create rollback checkpoint try: # Capture current system state current_state = await self._capture_system_state(action) checkpoint_id = await self.safety_framework["rollback_manager"].create_checkpoint( asdict(action), current_state ) if activity and run_record: activity.log_event( event_type="progress", severity="info", message="Rollback checkpoint created", payload={"checkpoint_id": checkpoint_id}, run_id=run_record.id, agent_type=self.agent_type, ) except Exception as e: logger.warning(f"Failed to create checkpoint: {e}") if activity and run_record: activity.log_event( event_type="warning", severity="warning", message=str(e)[:2000], payload={"checkpoint": "failed"}, run_id=run_record.id, agent_type=self.agent_type, ) # Continue execution even if checkpoint fails? Maybe not for critical actions. # For now, we log and proceed. # 3. Execute action (with MLflow tracing if enabled) if self.txtai_agent and self.txtai_agent.available: if self.enable_tracing and MLFLOW_AVAILABLE: with mlflow.start_run(run_name=f"{self.agent_type}_{action.action_type}"): mlflow.log_param("agent_id", self.agent_id) mlflow.log_param("action_type", action.action_type) mlflow.log_dict(action.parameters, "parameters.json") result = await self._execute_with_txtai(action) mlflow.log_text(str(result), "result.txt") else: result = await self._execute_with_txtai(action) else: result = await self._execute_fallback(action) # 4. Update performance metrics end_time = datetime.utcnow() response_time = (end_time - start_time).total_seconds() await self._update_performance_metrics(True, response_time) logger.info(f"Agent {self.agent_id} action completed successfully: {action.action_id}") if activity and run_record: activity.log_event( event_type="final_summary", severity="info", message=str(result)[:2000] if result is not None else None, payload={"action_id": action.action_id}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=True, result_summary=str(result)[:4000] if result is not None else None) return { "success": True, "result": result, "action_id": action.action_id, "agent_id": self.agent_id, "execution_time": response_time, "timestamp": end_time.isoformat() } except Exception as e: logger.error(f"Agent {self.agent_id} action failed: {action.action_id} - {e}") # 5. Handle failure and rollback if needed if checkpoint_id: logger.info(f"Initiating rollback to checkpoint {checkpoint_id}") await self.safety_framework["rollback_manager"].rollback_to_checkpoint(checkpoint_id) # Track failure in SIF if available if hasattr(self, 'sif_service') and self.sif_service: try: # Avoid circular import by checking attribute existence # Pass action dict as context await self.sif_service.track_agent_failure( agent_id=self.agent_id, error=e, context=asdict(action) ) except Exception as tracking_err: logger.warning(f"Failed to track agent failure in SIF: {tracking_err}") # Update performance metrics end_time = datetime.utcnow() response_time = (end_time - start_time).total_seconds() await self._update_performance_metrics(False, response_time) if self.enable_tracing and MLFLOW_AVAILABLE: mlflow.log_metric("success", 0) mlflow.log_param("error", str(e)) if activity and run_record: try: activity.log_event( event_type="error", severity="error", message=str(e)[:2000], payload={"action_id": action.action_id, "checkpoint_id": checkpoint_id}, run_id=run_record.id, agent_type=self.agent_type, ) activity.finish_run(run_record.id, success=False, error_message=str(e)[:4000]) activity.create_alert( alert_type="agent_action_failed", title=f"{self.agent_type}: {action.action_type} failed", message=str(e)[:2000], severity="error", payload={"agent_id": self.agent_id, "action_id": action.action_id, "action_type": action.action_type}, ) except Exception: pass return { "success": False, "error": str(e), "action_id": action.action_id, "agent_id": self.agent_id, "execution_time": response_time, "timestamp": end_time.isoformat(), "rollback_initiated": bool(checkpoint_id) } finally: try: if db: db.close() except Exception: pass async def _capture_system_state(self, action: AgentAction) -> Dict[str, Any]: """Capture current system state for rollback purposes""" state = {"timestamp": datetime.utcnow().isoformat()} try: # Determine state to capture based on action type # SEO Optimization (Check first to avoid being caught by generic 'optimize') if "seo" in action.action_type: state["seo_state"] = { "target": action.target_resource, "timestamp": datetime.utcnow().isoformat() } if "current_settings" in action.parameters: state["seo_state"]["settings"] = action.parameters["current_settings"] # Content Modification elif any(kw in action.action_type for kw in ["update", "rewrite", "optimize", "modify", "blog", "article"]): if "content_id" in action.parameters: # In a real implementation, fetch from DB using content_id # For now, we capture what we can from parameters or minimal state state["original_content"] = { "id": action.parameters.get("content_id"), "version": "pre_modification" } if "original_content" in action.parameters: state["original_content"]["data"] = action.parameters["original_content"] except Exception as e: logger.warning(f"Failed to capture detailed system state: {e}") return state async def _execute_with_txtai(self, action: AgentAction) -> str: """Execute action using txtai agent""" try: # Prepare prompt for txtai agent prompt = self._prepare_agent_prompt(action) # Execute with txtai agent via self.run logic result = await self.run(prompt) return result except Exception as e: logger.error(f"txtai agent execution failed: {e}") raise e async def _execute_fallback(self, action: AgentAction) -> str: """Execute fallback action when txtai is not available""" # Simulate agent processing for development logger.info(f"Executing fallback action: {action.action_type}") # Return simulated result based on action type if action.action_type == "analyze_competitor": return "Competitor analysis completed (fallback mode)" elif action.action_type == "optimize_content": return "Content optimization completed (fallback mode)" elif action.action_type == "fix_seo_issue": return "SEO issue fixed (fallback mode)" else: return f"Action {action.action_type} completed (fallback mode)" def _prepare_agent_prompt(self, action: AgentAction) -> str: """Prepare prompt for txtai agent""" return f""" You are the {self.agent_type} agent for ALwrity user {self.user_id}. Action Details: - Type: {action.action_type} - Target: {action.target_resource} - Parameters: {json.dumps(action.parameters, indent=2)} - Expected Outcome: {action.expected_outcome} - Risk Level: {action.risk_level} Please execute this action and provide a detailed response. Consider user goals, safety constraints, and potential impacts. """ async def _validate_action_safety(self, action: AgentAction) -> bool: """Validate action against safety constraints""" try: # Use SafetyConstraintManager from safety_framework validation_result = await self.safety_framework["constraint_manager"].validate_action(asdict(action)) if not validation_result.is_valid: logger.warning(f"Safety validation failed for action {action.action_id}: {validation_result.violations}") # Check if approval is required and handle it if validation_result.requires_approval: logger.info(f"Requesting approval for action {action.action_id}") await self.safety_framework["approval_system"].request_approval(asdict(action)) return False # Pending approval counts as false for immediate execution return False return True except Exception as e: logger.error(f"Error during safety validation: {e}") # Fail safe return False async def _update_performance_metrics(self, success: bool, response_time: float): """Update agent performance metrics""" self.performance.total_actions += 1 self.performance.last_action_at = datetime.utcnow().isoformat() if success: self.performance.successful_actions += 1 else: self.performance.failed_actions += 1 # Update average response time if self.performance.average_response_time == 0: self.performance.average_response_time = response_time else: self.performance.average_response_time = ( (self.performance.average_response_time * (self.performance.total_actions - 1) + response_time) / self.performance.total_actions ) # Update success rate if self.performance.total_actions > 0: self.performance.success_rate = ( self.performance.successful_actions / self.performance.total_actions ) # Calculate efficiency score (0.0 to 1.0) # Based on success rate and response time time_factor = min(1.0, 30.0 / max(self.performance.average_response_time, 1.0)) self.performance.efficiency_score = ( self.performance.success_rate * 0.7 + time_factor * 0.3 ) def get_performance_metrics(self) -> AgentPerformance: """Get current performance metrics""" return self.performance async def get_current_status(self) -> Dict[str, Any]: """Get current agent status""" return { "agent_id": self.agent_id, "agent_type": self.agent_type, "user_id": self.user_id, "status": "active" if self.txtai_agent else "fallback", "performance": asdict(self.performance), "last_updated": datetime.utcnow().isoformat() } class StrategyOrchestratorAgent(BaseALwrityAgent): """Central orchestrator agent that coordinates all marketing agents""" def __init__(self, user_id: str, market_detector: Any = None, performance_monitor: Any = None, llm: Any = None): super().__init__(user_id, "StrategyOrchestrator", llm=llm) self.market_detector = market_detector self.performance_monitor = performance_monitor self.sub_agents = {} self.active_strategies = [] def set_sub_agents(self, agents: Dict[str, Any]): """Set available sub-agents""" self.sub_agents = agents def _create_txtai_agent(self) -> Agent: """Create txtai orchestrator agent with coordination tools""" if not TXTAI_AVAILABLE: return None return Agent( llm=self.llm, tools=[ { "name": "market_signal_detector", "description": "Detects market changes and competitor activities", "target": self._market_signal_detector_tool }, { "name": "google_trends_fetcher", "description": "Fetches Google Trends data and embeds it into SIF for retrieval", "target": self._google_trends_fetcher_tool }, { "name": "agent_coordinator", "description": "Coordinates actions between multiple agents", "target": self._agent_coordinator_tool }, { "name": "performance_analyzer", "description": "Analyzes marketing performance metrics", "target": self._performance_analyzer_tool }, { "name": "strategy_synthesizer", "description": "Synthesizes unified strategies from multiple inputs", "target": self._strategy_synthesizer_tool }, { "name": "task_delegator", "description": "Delegates specific tasks to specialized agents (content, competitor, seo, social)", "target": self._delegate_task_tool } ], max_iterations=15, system=self.get_effective_system_prompt(f"""You are the Marketing Strategy Orchestrator for ALwrity user {self.user_id}. Your role is to coordinate all marketing agents, analyze market signals, and synthesize unified strategies. Key Responsibility: DELEGATE tasks to specialized agents. - Content Strategy Agent: For content analysis, gaps, and optimization. - Competitor Response Agent: For monitoring and counter-strategies. - SEO Optimization Agent: For technical SEO and keywords. - Social Amplification Agent: For social trends and distribution. Use the 'task_delegator' tool to assign work to these agents. Do not just plan; EXECUTE by delegating. Always prioritize user goals and maintain safety constraints. Coordinate multi-agent responses to market changes effectively.""" ) ) async def _market_signal_detector_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: """Tool for detecting market signals""" try: signals = [] if self.market_detector: signals = await self.market_detector.detect_market_signals() return { "signals_detected": len(signals), "latest_signals": [s.dict() for s in signals[-5:]] if signals else [], "threat_level": self._assess_threat_level(signals), "timestamp": datetime.utcnow().isoformat() } except Exception as e: return {"error": str(e), "signals": []} async def _google_trends_fetcher_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: try: keywords = context.get("keywords") or [] timeframe = context.get("timeframe") or "today 12-m" geo = context.get("geo") or "US" if not isinstance(keywords, list): keywords = [str(keywords)] keywords = [str(k).strip() for k in keywords if str(k).strip()] if not keywords: return {"error": "keywords is required", "success": False} from services.research.trends.google_trends_service import GoogleTrendsService from services.intelligence.txtai_service import TxtaiIntelligenceService trends = await GoogleTrendsService().analyze_trends( keywords=keywords, timeframe=timeframe, geo=geo, user_id=self.user_id, ) run_id = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") latest_id = f"market_trends_latest:{self.user_id}" run_doc_id = f"market_trends_run:{self.user_id}:{run_id}" summary = ( f"LATEST Market Trends for {geo} ({timeframe}). Keywords: {', '.join(trends.get('keywords', keywords))}. " f"Related queries top: {len((trends.get('related_queries') or {}).get('top', []))}. " f"Related topics top: {len((trends.get('related_topics') or {}).get('top', []))}." ) metadata = { "type": "market_trends", "user_id": self.user_id, "run_id": run_id, "run_timestamp": trends.get("timestamp") or datetime.utcnow().isoformat(), "timeframe": timeframe, "geo": geo, "keywords": trends.get("keywords", keywords), "is_latest": True, "full_report": trends, } intelligence = TxtaiIntelligenceService(self.user_id) await intelligence.index_content( [ (latest_id, summary, metadata), (run_doc_id, summary, {**metadata, "is_latest": False}), ] ) return { "success": True, "run_id": run_id, "latest_doc_id": latest_id, "run_doc_id": run_doc_id, "keywords": trends.get("keywords", keywords), "geo": geo, "timeframe": timeframe, "timestamp": datetime.utcnow().isoformat(), } except Exception as e: return {"success": False, "error": str(e)} async def _agent_coordinator_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: """Tool for coordinating agent actions""" return { "agents_available": list(self.sub_agents.keys()), "coordination_status": "active", "last_coordination": datetime.utcnow().isoformat() } async def _performance_analyzer_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: """Tool for analyzing performance metrics""" try: perf_data = {} if self.performance_monitor: perf_data = self.performance_monitor.get_all_agents_performance() return { "overall_performance": perf_data, "agent_efficiency": self.performance.efficiency_score, "recommendations": ["Optimize content agent latency", "Increase SEO agent throughput"], "timestamp": datetime.utcnow().isoformat() } except Exception as e: return {"error": str(e)} async def _strategy_synthesizer_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: """Tool for synthesizing strategies""" return { "strategies_active": len(self.active_strategies), "synthesis_capability": "ready", "unified_strategy": "Focus on high-engagement topics while monitoring competitor X", "last_synthesis": datetime.utcnow().isoformat() } async def _delegate_task_tool(self, context: Dict[str, Any]) -> Dict[str, Any]: """ Tool to delegate a specific task to a specialized agent. Expected context keys: 'agent_name', 'instruction', 'task_context' """ agent_name = context.get('agent_name') instruction = context.get('instruction') task_context = context.get('task_context', {}) if not agent_name or not instruction: return {"error": "Missing agent_name or instruction"} agent = self.sub_agents.get(agent_name) if not agent: return {"error": f"Agent {agent_name} not available. Available: {list(self.sub_agents.keys())}"} try: # Delegate execution to the sub-agent logger.info(f"Delegating task to {agent_name}: {instruction}") sub_agent_prompt = None if hasattr(agent, "build_task_prompt"): try: sub_agent_prompt = agent.build_task_prompt(instruction=instruction, task_context=task_context) except Exception: sub_agent_prompt = None if not sub_agent_prompt: sub_agent_prompt = f"Task: {instruction}\nContext: {json.dumps(task_context)}\n\nPlease execute this task using your specialized tools and provide a detailed report." # Execute the agent result = await agent.run(sub_agent_prompt) return { "status": "success", "agent": agent_name, "result": result, "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"Delegation to {agent_name} failed: {e}") return {"error": str(e)} def _assess_threat_level(self, signals: List[Any] = None) -> str: """Assess current threat level based on market signals""" if not signals: return "low" critical_count = len([s for s in signals if getattr(s, 'urgency_level', 'low') == 'critical']) if critical_count > 0: return "critical" high_count = len([s for s in signals if getattr(s, 'urgency_level', 'low') == 'high']) if high_count > 2: return "high" return "moderate" # Global agent service instance (Deprecated, use agent_orchestrator.py) # This file now focuses on core definitions