Recovered critical missing components: PerformanceMonitor, MarketSignalDetector, and SemanticDashboard

This commit is contained in:
ajaysi
2026-02-08 14:06:09 +05:30
parent e404a86502
commit 43e66835ac
3 changed files with 1837 additions and 165 deletions

View File

@@ -172,79 +172,646 @@ class MarketSignalDetector:
return prioritized_signals return prioritized_signals
except Exception as e: except Exception as e:
logger.error(f"Error detecting market signals: {str(e)}") logger.error(f"Error detecting market signals for user {self.user_id}: {e}")
return [] return []
async def _get_signal_context(self) -> SignalContext: async def _get_signal_context(self) -> SignalContext:
"""Fetch current context for signal detection""" """Get comprehensive context for signal detection"""
# Placeholder implementation try:
return SignalContext( # Get semantic health
user_id=self.user_id, semantic_health = await self.semantic_monitor.check_semantic_health(self.user_id)
competitor_data={},
semantic_health={},
seo_performance={},
content_analysis={},
historical_data={}
)
def _is_cache_valid(self, signals: List[MarketSignal]) -> bool: # Get competitor data
"""Check if cached signals are still valid""" competitor_data = await self._get_competitor_data()
if not signals:
return False # Get SEO performance
# Basic check for now seo_performance = await self._get_seo_performance()
return True
# Get content analysis
content_analysis = await self._get_content_analysis()
# Get historical data
historical_data = await self._get_historical_data()
return SignalContext(
user_id=self.user_id,
competitor_data=competitor_data,
semantic_health=semantic_health,
seo_performance=seo_performance,
content_analysis=content_analysis,
historical_data=historical_data
)
except Exception as e:
logger.error(f"Error getting signal context for user {self.user_id}: {e}")
# Return minimal context
return SignalContext(
user_id=self.user_id,
competitor_data={},
semantic_health={},
seo_performance={},
content_analysis={},
historical_data={}
)
async def _detect_competitor_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_competitor_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from competitor activities""" """Detect competitor-related market signals"""
return [] signals = []
try:
competitor_data = context.competitor_data.get('competitors', [])
for competitor in competitor_data:
competitor_id = competitor.get('id')
competitor_name = competitor.get('name', 'Unknown Competitor')
# Check for significant changes in competitor metrics
current_metrics = {
'content_volume': competitor.get('content_volume', 0),
'semantic_overlap': competitor.get('semantic_overlap', 0),
'authority_score': competitor.get('authority_score', 0),
'trending_topics': len(competitor.get('trending_topics', []))
}
# Compare with baseline metrics
baseline_key = f"competitor_{competitor_id}"
baseline = self.baseline_metrics.get(baseline_key, current_metrics)
# Detect significant changes
for metric, current_value in current_metrics.items():
baseline_value = baseline.get(metric, current_value)
change_percentage = abs(current_value - baseline_value) / max(baseline_value, 1)
if change_percentage > self.thresholds['competitor_change_threshold']:
signal = MarketSignal(
signal_id=f"competitor_{competitor_id}_{metric}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.COMPETITOR_CHANGE,
source=competitor_name,
description=f"Competitor {competitor_name} shows significant change in {metric}: {change_percentage:.1%}",
impact_score=min(0.9, change_percentage * 2), # Cap at 0.9
urgency_level=self._determine_urgency(change_percentage),
confidence_score=0.8,
related_topics=competitor.get('trending_topics', [])[:3],
suggested_actions=self._get_competitor_response_actions(metric, change_percentage),
metadata={
'competitor_id': competitor_id,
'metric': metric,
'old_value': baseline_value,
'new_value': current_value,
'change_percentage': change_percentage
}
)
signals.append(signal)
# Update baseline
self.baseline_metrics[baseline_key] = current_metrics
except Exception as e:
logger.error(f"Error detecting competitor signals: {e}")
return signals
async def _detect_serp_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_serp_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from SERP changes""" """Detect SERP-related market signals"""
return [] signals = []
try:
seo_performance = context.seo_performance
# Check for significant SERP position changes
serp_changes = seo_performance.get('serp_changes', [])
for change in serp_changes:
keyword = change.get('keyword')
old_position = change.get('old_position', 100)
new_position = change.get('new_position', 100)
# Calculate position change
position_change = old_position - new_position # Positive = improvement
change_percentage = abs(position_change) / max(old_position, 1)
if change_percentage > self.thresholds['serp_fluctuation_threshold']:
if position_change > 0: # Improvement
description = f"Significant SERP improvement for '{keyword}': moved from {old_position} to {new_position}"
impact_score = min(0.8, change_percentage * 1.5)
urgency_level = UrgencyLevel.LOW
suggested_actions = ["Monitor trend", "Capitalize on improvement"]
else: # Decline
description = f"Significant SERP decline for '{keyword}': dropped from {old_position} to {new_position}"
impact_score = min(0.9, change_percentage * 2)
urgency_level = UrgencyLevel.HIGH
suggested_actions = ["Investigate cause", "Optimize content", "Check technical SEO"]
signal = MarketSignal(
signal_id=f"serp_{keyword.replace(' ', '_')}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.SERP_FLUCTUATION,
source="SERP Analysis",
description=description,
impact_score=impact_score,
urgency_level=urgency_level,
confidence_score=0.85,
related_topics=[keyword],
suggested_actions=suggested_actions,
metadata={
'keyword': keyword,
'old_position': old_position,
'new_position': new_position,
'position_change': position_change,
'change_percentage': change_percentage
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting SERP signals: {e}")
return signals
async def _detect_social_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_social_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from social trends""" """Detect social media trend signals"""
return [] signals = []
try:
# Get social media data
social_data = context.historical_data.get('social_metrics', {})
# Check for trending topics
trending_topics = social_data.get('trending_topics', [])
for topic in trending_topics:
topic_name = topic.get('topic')
engagement_rate = topic.get('engagement_rate', 0)
trend_score = topic.get('trend_score', 0)
if trend_score > self.thresholds['social_trend_threshold']:
signal = MarketSignal(
signal_id=f"social_{topic_name.replace(' ', '_')}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.SOCIAL_TREND,
source="Social Media Analysis",
description=f"Social trend detected: '{topic_name}' with engagement rate {engagement_rate:.2%}",
impact_score=min(0.8, trend_score * 1.5),
urgency_level=self._determine_urgency(trend_score),
confidence_score=0.75,
related_topics=[topic_name],
suggested_actions=["Create content on trending topic", "Monitor trend development", "Engage with trend"],
metadata={
'topic': topic_name,
'engagement_rate': engagement_rate,
'trend_score': trend_score,
'platforms': topic.get('platforms', [])
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting social signals: {e}")
return signals
async def _detect_industry_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_industry_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from industry news""" """Detect industry news and trend signals"""
return [] signals = []
try:
# Get industry data
industry_data = context.historical_data.get('industry_news', {})
# Check for significant industry developments
news_items = industry_data.get('recent_news', [])
for news in news_items:
news_title = news.get('title', 'Industry News')
relevance_score = news.get('relevance_score', 0)
impact_assessment = news.get('impact_assessment', 'medium')
if relevance_score > 0.6: # High relevance to user's industry
signal = MarketSignal(
signal_id=f"industry_{hash(news_title) % 10000}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.INDUSTRY_NEWS,
source="Industry News Analysis",
description=f"Industry development: {news_title}",
impact_score=min(0.9, relevance_score * 1.2),
urgency_level=self._map_impact_to_urgency(impact_assessment),
confidence_score=0.8,
related_topics=news.get('related_topics', []),
suggested_actions=["Analyze industry impact", "Adjust strategy if needed", "Monitor competitor response"],
metadata={
'news_title': news_title,
'relevance_score': relevance_score,
'impact_assessment': impact_assessment,
'news_date': news.get('date'),
'source': news.get('source')
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting industry signals: {e}")
return signals
async def _detect_performance_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_performance_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from site performance""" """Detect performance change signals"""
return [] signals = []
try:
# Get performance data
performance_data = context.historical_data.get('performance_metrics', {})
# Check for significant changes in key metrics
current_metrics = {
'traffic': performance_data.get('current_traffic', 0),
'engagement': performance_data.get('current_engagement', 0),
'conversion_rate': performance_data.get('current_conversion_rate', 0),
'bounce_rate': performance_data.get('current_bounce_rate', 0)
}
# Compare with historical baseline
baseline_metrics = performance_data.get('baseline_metrics', current_metrics)
for metric, current_value in current_metrics.items():
baseline_value = baseline_metrics.get(metric, current_value)
if baseline_value > 0: # Avoid division by zero
change_percentage = abs(current_value - baseline_value) / baseline_value
if change_percentage > self.thresholds['performance_change_threshold']:
if current_value > baseline_value: # Improvement
description = f"Performance improvement detected: {metric} increased by {change_percentage:.1%}"
impact_score = min(0.7, change_percentage * 1.5)
urgency_level = UrgencyLevel.LOW
suggested_actions = ["Monitor trend", "Analyze success factors", "Scale successful strategies"]
else: # Decline
description = f"Performance decline detected: {metric} decreased by {change_percentage:.1%}"
impact_score = min(0.9, change_percentage * 2)
urgency_level = UrgencyLevel.HIGH
suggested_actions = ["Investigate cause", "Implement corrective measures", "Monitor recovery"]
signal = MarketSignal(
signal_id=f"performance_{metric}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.PERFORMANCE_CHANGE,
source="Performance Analytics",
description=description,
impact_score=impact_score,
urgency_level=urgency_level,
confidence_score=0.9,
related_topics=[metric],
suggested_actions=suggested_actions,
metadata={
'metric': metric,
'old_value': baseline_value,
'new_value': current_value,
'change_percentage': change_percentage,
'trend_direction': 'up' if current_value > baseline_value else 'down'
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting performance signals: {e}")
return signals
async def _detect_content_gap_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_content_gap_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from content gaps""" """Detect content gap signals"""
return [] signals = []
try:
semantic_health = context.semantic_health
# Check for significant semantic gaps
semantic_gaps = semantic_health.get('semantic_gaps', [])
for gap in semantic_gaps:
gap_topic = gap.get('topic')
gap_score = gap.get('gap_score', 0)
competitor_coverage = gap.get('competitor_coverage', 0)
if gap_score > self.thresholds['content_gap_threshold']:
signal = MarketSignal(
signal_id=f"content_gap_{gap_topic.replace(' ', '_')}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.CONTENT_GAP,
source="Semantic Analysis",
description=f"Content gap identified: '{gap_topic}' with gap score {gap_score:.2f}",
impact_score=min(0.8, gap_score * 1.5),
urgency_level=self._determine_urgency(gap_score),
confidence_score=0.85,
related_topics=[gap_topic],
suggested_actions=["Create content on gap topic", "Analyze competitor approach", "Optimize existing content"],
metadata={
'gap_topic': gap_topic,
'gap_score': gap_score,
'competitor_coverage': competitor_coverage,
'semantic_similarity': gap.get('semantic_similarity', 0)
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting content gap signals: {e}")
return signals
async def _detect_seo_opportunity_signals(self, context: SignalContext) -> List[MarketSignal]: async def _detect_seo_opportunity_signals(self, context: SignalContext) -> List[MarketSignal]:
"""Detect signals from SEO opportunities""" """Detect SEO opportunity signals"""
return [] signals = []
try:
seo_performance = context.seo_performance
# Check for SEO opportunities
seo_opportunities = seo_performance.get('opportunities', [])
for opportunity in seo_opportunities:
opportunity_type = opportunity.get('type')
opportunity_score = opportunity.get('opportunity_score', 0)
estimated_impact = opportunity.get('estimated_impact', 'medium')
if opportunity_score > self.thresholds['seo_opportunity_threshold']:
signal = MarketSignal(
signal_id=f"seo_opportunity_{opportunity_type}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
signal_type=SignalType.SEO_OPPORTUNITY,
source="SEO Analysis",
description=f"SEO opportunity identified: {opportunity_type} with score {opportunity_score:.2f}",
impact_score=min(0.8, opportunity_score * 1.5),
urgency_level=self._map_impact_to_urgency(estimated_impact),
confidence_score=0.8,
related_topics=opportunity.get('related_keywords', []),
suggested_actions=["Implement SEO recommendation", "Monitor impact", "Scale successful optimizations"],
metadata={
'opportunity_type': opportunity_type,
'opportunity_score': opportunity_score,
'estimated_impact': estimated_impact,
'implementation_effort': opportunity.get('implementation_effort', 'medium'),
'priority_score': opportunity.get('priority_score', 0)
}
)
signals.append(signal)
except Exception as e:
logger.error(f"Error detecting SEO opportunity signals: {e}")
return signals
# Helper methods
def _determine_urgency(self, score: float) -> UrgencyLevel:
"""Determine urgency level based on score"""
if score >= 0.8:
return UrgencyLevel.CRITICAL
elif score >= 0.6:
return UrgencyLevel.HIGH
elif score >= 0.3:
return UrgencyLevel.MEDIUM
else:
return UrgencyLevel.LOW
def _map_impact_to_urgency(self, impact: str) -> UrgencyLevel:
"""Map impact assessment to urgency level"""
impact_map = {
'critical': UrgencyLevel.CRITICAL,
'high': UrgencyLevel.HIGH,
'medium': UrgencyLevel.MEDIUM,
'low': UrgencyLevel.LOW
}
return impact_map.get(impact.lower(), UrgencyLevel.MEDIUM)
def _get_competitor_response_actions(self, metric: str, change_percentage: float) -> List[str]:
"""Get suggested actions for competitor changes"""
actions = []
if metric == 'content_volume':
if change_percentage > 0:
actions = ["Analyze competitor content strategy", "Identify content gaps", "Increase content production"]
else:
actions = ["Monitor competitor focus shift", "Identify new opportunities", "Maintain content quality"]
elif metric == 'semantic_overlap':
if change_percentage > 0:
actions = ["Differentiate content strategy", "Find unique angles", "Avoid keyword cannibalization"]
else:
actions = ["Explore new topics", "Expand content coverage", "Monitor competitor positioning"]
elif metric == 'authority_score':
if change_percentage > 0:
actions = ["Analyze competitor backlink strategy", "Improve content quality", "Build domain authority"]
else:
actions = ["Capitalize on competitor weakness", "Strengthen own authority", "Monitor recovery"]
else:
actions = ["Monitor competitor activity", "Analyze impact on market", "Adjust strategy if needed"]
return actions
def _filter_signals(self, signals: List[MarketSignal]) -> List[MarketSignal]: def _filter_signals(self, signals: List[MarketSignal]) -> List[MarketSignal]:
"""Filter out low-quality or duplicate signals""" """Filter signals based on relevance and quality"""
return signals filtered = []
for signal in signals:
# Skip low confidence signals
if signal.confidence_score < 0.5:
continue
# Skip expired signals
if self._is_signal_expired(signal):
continue
# Skip duplicate signals (same type and source within short timeframe)
if self._is_duplicate_signal(signal, filtered):
continue
filtered.append(signal)
return filtered
def _prioritize_signals(self, signals: List[MarketSignal]) -> List[MarketSignal]: def _prioritize_signals(self, signals: List[MarketSignal]) -> List[MarketSignal]:
"""Prioritize signals based on impact and urgency""" """Prioritize signals based on impact and urgency"""
return sorted(signals, key=lambda x: (x.urgency_level.value, x.impact_score), reverse=True) # Sort by priority score (impact * urgency_weight)
def priority_score(signal: MarketSignal) -> float:
urgency_weights = {
UrgencyLevel.CRITICAL: 1.0,
UrgencyLevel.HIGH: 0.8,
UrgencyLevel.MEDIUM: 0.5,
UrgencyLevel.LOW: 0.2
}
urgency_weight = urgency_weights.get(signal.urgency_level, 0.5)
return signal.impact_score * urgency_weight * signal.confidence_score
return sorted(signals, key=priority_score, reverse=True)
def _is_signal_expired(self, signal: MarketSignal) -> bool:
"""Check if signal has expired"""
try:
expires_at = datetime.fromisoformat(signal.expires_at)
return datetime.utcnow() > expires_at
except:
return False
def _is_duplicate_signal(self, signal: MarketSignal, existing_signals: List[MarketSignal]) -> bool:
"""Check if signal is a duplicate of recent signals"""
try:
signal_time = datetime.fromisoformat(signal.detected_at)
for existing in existing_signals:
if (existing.signal_type == signal.signal_type and
existing.source == signal.source and
existing.related_topics == signal.related_topics):
# Check if within 1 hour
existing_time = datetime.fromisoformat(existing.detected_at)
if abs((signal_time - existing_time).total_seconds()) < 3600:
return True
return False
except:
return False
def _is_cache_valid(self, cached_signals: List[MarketSignal]) -> bool:
"""Check if cached signals are still valid"""
if not cached_signals:
return False
try:
# Check if any signal is still valid (not expired)
for signal in cached_signals:
if not self._is_signal_expired(signal):
return True
return False
except:
return False
def _trim_signal_history(self): def _trim_signal_history(self):
"""Keep signal history within limits""" """Trim signal history to keep only recent signals"""
if len(self.signal_history) > 1000: cutoff_time = datetime.utcnow().timestamp() - (7 * 24 * 60 * 60) # 7 days
self.signal_history = self.signal_history[-1000:]
class MarketTrendAnalyzer: self.signal_history = [
""" signal for signal in self.signal_history
Analyzer for detecting market trends from aggregated signals. if datetime.fromisoformat(signal.detected_at).timestamp() > cutoff_time
""" ]
def __init__(self, user_id: str):
self.user_id = user_id
self.detector = MarketSignalDetector(user_id)
async def analyze_trends(self, context: Optional[Dict[str, Any]] = None) -> List[MarketSignal]: # Data retrieval methods (to be implemented with actual ALwrity services)
"""Analyze current market trends"""
# Placeholder implementation async def _get_competitor_data(self) -> Dict[str, Any]:
logger.info(f"Analyzing market trends for user {self.user_id}") """Get competitor data from existing services"""
return [] # This will be implemented to integrate with existing competitor analysis
return {
'competitors': [],
'analysis_timestamp': datetime.utcnow().isoformat()
}
async def _get_seo_performance(self) -> Dict[str, Any]:
"""Get SEO performance data"""
# This will be implemented to integrate with existing SEO analysis
return {
'serp_changes': [],
'opportunities': [],
'analysis_timestamp': datetime.utcnow().isoformat()
}
async def _get_content_analysis(self) -> Dict[str, Any]:
"""Get content analysis data"""
# This will be implemented to integrate with existing content analysis
return {
'content_metrics': {},
'semantic_analysis': {},
'analysis_timestamp': datetime.utcnow().isoformat()
}
async def _get_historical_data(self) -> Dict[str, Any]:
"""Get historical data for trend analysis"""
# This will be implemented to get historical performance data
return {
'performance_metrics': {},
'social_metrics': {},
'industry_news': [],
'data_timestamp': datetime.utcnow().isoformat()
}
# Service class for market signal detection
class MarketSignalService:
"""Service class for market signal detection operations"""
def __init__(self):
self.detectors: Dict[str, MarketSignalDetector] = {}
self.signal_history: Dict[str, List[MarketSignal]] = {}
async def get_detector(self, user_id: str) -> MarketSignalDetector:
"""Get or create a market signal detector for a user"""
if user_id not in self.detectors:
self.detectors[user_id] = MarketSignalDetector(user_id)
return self.detectors[user_id]
async def detect_signals_for_user(self, user_id: str) -> List[MarketSignal]:
"""Detect market signals for a specific user"""
detector = await self.get_detector(user_id)
signals = await detector.detect_market_signals()
# Store in history
if user_id not in self.signal_history:
self.signal_history[user_id] = []
self.signal_history[user_id].extend(signals)
return signals
async def get_signal_summary(self, user_id: str) -> Dict[str, Any]:
"""Get summary of recent signals for a user"""
detector = await self.get_detector(user_id)
signals = await detector.detect_market_signals()
# Group by signal type
signals_by_type = {}
for signal in signals:
signal_type = signal.signal_type.value
if signal_type not in signals_by_type:
signals_by_type[signal_type] = []
signals_by_type[signal_type].append(signal)
# Calculate summary metrics
total_signals = len(signals)
high_priority_signals = len([s for s in signals if s.urgency_level in [UrgencyLevel.HIGH, UrgencyLevel.CRITICAL]])
average_impact_score = sum(s.impact_score for s in signals) / max(total_signals, 1)
return {
'user_id': user_id,
'total_signals': total_signals,
'high_priority_signals': high_priority_signals,
'average_impact_score': average_impact_score,
'signals_by_type': signals_by_type,
'latest_signals': signals[:5], # Top 5 most recent
'timestamp': datetime.utcnow().isoformat()
}
async def get_active_signals(self, user_id: str) -> List[MarketSignal]:
"""Get active (non-expired) signals for a user"""
detector = await self.get_detector(user_id)
all_signals = await detector.detect_market_signals()
# Filter active signals
active_signals = []
for signal in all_signals:
try:
expires_at = datetime.fromisoformat(signal.expires_at)
if datetime.utcnow() <= expires_at:
active_signals.append(signal)
except:
continue
return active_signals
# Global service instance
market_signal_service = MarketSignalService()
# Convenience functions
async def detect_market_signals(user_id: str) -> List[MarketSignal]:
"""Detect market signals for a user"""
return await market_signal_service.detect_signals_for_user(user_id)
async def get_market_signal_summary(user_id: str) -> Dict[str, Any]:
"""Get market signal summary for a user"""
return await market_signal_service.get_signal_summary(user_id)
async def get_active_market_signals(user_id: str) -> List[MarketSignal]:
"""Get active market signals for a user"""
return await market_signal_service.get_active_signals(user_id)

View File

@@ -17,112 +17,747 @@ from services.database import get_session_for_user
logger = get_service_logger(__name__) logger = get_service_logger(__name__)
class AgentStatus(Enum):
IDLE = "idle"
BUSY = "busy"
ERROR = "error"
OFFLINE = "offline"
INITIALIZING = "initializing"
class PerformanceMetric(Enum): class PerformanceMetric(Enum):
"""Types of performance metrics tracked"""
RESPONSE_TIME = "response_time" RESPONSE_TIME = "response_time"
SUCCESS_RATE = "success_rate" SUCCESS_RATE = "success_rate"
TOKEN_USAGE = "token_usage" EFFICIENCY_SCORE = "efficiency_score"
COST_PER_ACTION = "cost_per_action" RESOURCE_USAGE = "resource_usage"
RESOURCE_UTILIZATION = "resource_utilization" USER_SATISFACTION = "user_satisfaction"
GOAL_COMPLETION_RATE = "goal_completion_rate" MARKET_IMPACT = "market_impact"
class AgentStatus(Enum):
"""Status of agent operations"""
ACTIVE = "active"
IDLE = "idle"
PROCESSING = "processing"
ERROR = "error"
MAINTENANCE = "maintenance"
@dataclass @dataclass
class AgentPerformanceMetrics: class PerformanceDataPoint:
agent_id: str """Single performance data point"""
timestamp: datetime timestamp: str
metrics: Dict[str, float] metric_type: PerformanceMetric
value: float
context: Dict[str, Any] context: Dict[str, Any]
agent_id: str
user_id: str
class PerformanceMonitor: @dataclass
""" class AgentPerformanceSnapshot:
Monitors and analyzes agent performance metrics """Complete performance snapshot for an agent"""
""" agent_id: str
user_id: str
timestamp: str
status: AgentStatus
total_actions: int
successful_actions: int
failed_actions: int
average_response_time: float
success_rate: float
efficiency_score: float
resource_usage: Dict[str, float]
market_impact_score: float
last_action_at: str
def __init__(self): def __post_init__(self):
self.metrics_buffer = deque(maxlen=1000) if self.timestamp is None:
self.performance_history = defaultdict(list) self.timestamp = datetime.utcnow().isoformat()
self.alert_thresholds = {
PerformanceMetric.SUCCESS_RATE: 0.8, # Alert if success rate < 80% @dataclass
PerformanceMetric.RESPONSE_TIME: 30.0, # Alert if response time > 30s class PerformanceTrend:
PerformanceMetric.GOAL_COMPLETION_RATE: 0.7 # Alert if completion < 70% """Performance trend analysis"""
metric_type: PerformanceMetric
trend_direction: str # "improving", "declining", "stable"
trend_strength: float # 0.0 to 1.0
change_rate: float # Percentage change per time unit
confidence: float # 0.0 to 1.0
period_start: str
period_end: str
@dataclass
class OptimizationRecommendation:
"""Performance optimization recommendation"""
recommendation_id: str
agent_id: str
user_id: str
recommendation_type: str
priority: str # "high", "medium", "low"
description: str
expected_impact: float # Expected improvement in performance
implementation_steps: List[str]
estimated_effort: str # "low", "medium", "high"
created_at: str
expires_at: str
def __post_init__(self):
if self.created_at is None:
self.created_at = datetime.utcnow().isoformat()
if self.expires_at is None:
# Default expiration: 7 days
expires = datetime.utcnow().timestamp() + (7 * 24 * 60 * 60)
self.expires_at = datetime.fromtimestamp(expires).isoformat()
class AgentPerformanceMonitor:
"""Main performance monitoring system for agents"""
def __init__(self, user_id: str):
self.user_id = user_id
self.performance_data: Dict[str, List[PerformanceDataPoint]] = defaultdict(list)
self.agent_snapshots: Dict[str, AgentPerformanceSnapshot] = {}
self.recommendations: List[OptimizationRecommendation] = []
self.performance_history: deque = deque(maxlen=1000) # Keep last 1000 data points
# Performance thresholds and targets
self.performance_targets = {
"success_rate": 0.85, # 85% success rate target
"response_time": 30.0, # 30 seconds average response time target
"efficiency_score": 0.75, # 75% efficiency score target
"market_impact": 0.60 # 60% market impact score target
} }
async def record_metric(self, # Alert thresholds
agent_id: str, self.alert_thresholds = {
metric_type: PerformanceMetric, "success_rate": 0.70, # Alert if below 70%
value: float, "response_time": 60.0, # Alert if above 60 seconds
context: Optional[Dict[str, Any]] = None): "efficiency_score": 0.50, # Alert if below 50%
"""Record a performance metric for an agent""" "market_impact": 0.30 # Alert if below 30%
metric_entry = AgentPerformanceMetrics( }
agent_id=agent_id,
timestamp=datetime.utcnow(),
metrics={metric_type.value: value},
context=context or {}
)
self.metrics_buffer.append(metric_entry) logger.info(f"Initialized AgentPerformanceMonitor for user: {user_id}")
self.performance_history[agent_id].append(metric_entry)
# Check thresholds async def record_performance_data(self, agent_id: str, metric_type: PerformanceMetric, value: float, context: Dict[str, Any] = None) -> bool:
await self._check_thresholds(agent_id, metric_type, value) """Record a performance data point"""
try:
if context is None:
context = {}
# Persist if needed (batching implemented in production) data_point = PerformanceDataPoint(
# await self._persist_metric(metric_entry) timestamp=datetime.utcnow().isoformat(),
metric_type=metric_type,
value=value,
context=context,
agent_id=agent_id,
user_id=self.user_id
)
async def get_agent_performance(self, agent_id: str, time_window_minutes: int = 60) -> Dict[str, Any]: # Store in performance data
"""Get aggregated performance metrics for an agent""" self.performance_data[agent_id].append(data_point)
cutoff_time = datetime.utcnow() - timedelta(minutes=time_window_minutes) self.performance_history.append(data_point)
relevant_metrics = [
m for m in self.performance_history[agent_id]
if m.timestamp > cutoff_time
]
if not relevant_metrics: # Keep only recent data (last 24 hours for real-time analysis)
cutoff_time = datetime.utcnow().timestamp() - (24 * 60 * 60)
self.performance_data[agent_id] = [
dp for dp in self.performance_data[agent_id]
if datetime.fromisoformat(dp.timestamp).timestamp() > cutoff_time
]
logger.debug(f"Recorded performance data for agent {agent_id}: {metric_type.value} = {value}")
return True
except Exception as e:
logger.error(f"Error recording performance data for agent {agent_id}: {e}")
return False
async def update_agent_snapshot(self, agent_id: str, status: AgentStatus, action_result: Dict[str, Any] = None) -> AgentPerformanceSnapshot:
"""Update performance snapshot for an agent"""
try:
# Get recent performance data
recent_data = self.performance_data[agent_id]
# Calculate metrics from recent data
total_actions = len([dp for dp in recent_data if dp.metric_type == PerformanceMetric.SUCCESS_RATE])
successful_actions = len([dp for dp in recent_data if dp.metric_type == PerformanceMetric.SUCCESS_RATE and dp.value > 0.5])
failed_actions = total_actions - successful_actions
# Calculate average response time
response_time_data = [dp.value for dp in recent_data if dp.metric_type == PerformanceMetric.RESPONSE_TIME]
avg_response_time = sum(response_time_data) / len(response_time_data) if response_time_data else 0.0
# Calculate success rate
success_rate = successful_actions / total_actions if total_actions > 0 else 0.0
# Calculate efficiency score
efficiency_data = [dp.value for dp in recent_data if dp.metric_type == PerformanceMetric.EFFICIENCY_SCORE]
avg_efficiency = sum(efficiency_data) / len(efficiency_data) if efficiency_data else 0.0
# Calculate market impact
market_impact_data = [dp.value for dp in recent_data if dp.metric_type == PerformanceMetric.MARKET_IMPACT]
avg_market_impact = sum(market_impact_data) / len(market_impact_data) if market_impact_data else 0.0
# Get resource usage
resource_usage = self._calculate_resource_usage(agent_id, recent_data)
# Get last action time
last_action_at = max([dp.timestamp for dp in recent_data], default=datetime.utcnow().isoformat()) if recent_data else datetime.utcnow().isoformat()
# Create snapshot
snapshot = AgentPerformanceSnapshot(
agent_id=agent_id,
user_id=self.user_id,
timestamp=datetime.utcnow().isoformat(),
status=status,
total_actions=total_actions,
successful_actions=successful_actions,
failed_actions=failed_actions,
average_response_time=avg_response_time,
success_rate=success_rate,
efficiency_score=avg_efficiency,
resource_usage=resource_usage,
market_impact_score=avg_market_impact,
last_action_at=last_action_at
)
self.agent_snapshots[agent_id] = snapshot
logger.info(f"Updated performance snapshot for agent {agent_id}: success_rate={success_rate:.2f}, efficiency={avg_efficiency:.2f}")
return snapshot
except Exception as e:
logger.error(f"Error updating performance snapshot for agent {agent_id}: {e}")
# Return a default snapshot
return AgentPerformanceSnapshot(
agent_id=agent_id,
user_id=self.user_id,
timestamp=datetime.utcnow().isoformat(),
status=AgentStatus.ERROR,
total_actions=0,
successful_actions=0,
failed_actions=0,
average_response_time=0.0,
success_rate=0.0,
efficiency_score=0.0,
resource_usage={},
market_impact_score=0.0,
last_action_at=datetime.utcnow().isoformat()
)
def _calculate_resource_usage(self, agent_id: str, recent_data: List[PerformanceDataPoint]) -> Dict[str, float]:
"""Calculate resource usage metrics"""
resource_usage = {
"cpu_usage": 0.0,
"memory_usage": 0.0,
"api_calls": 0,
"processing_time": 0.0
}
try:
# Extract resource usage from context
for dp in recent_data:
if dp.metric_type == PerformanceMetric.RESOURCE_USAGE and dp.context:
resource_usage["cpu_usage"] = max(resource_usage["cpu_usage"], dp.context.get("cpu_usage", 0.0))
resource_usage["memory_usage"] = max(resource_usage["memory_usage"], dp.context.get("memory_usage", 0.0))
resource_usage["api_calls"] += dp.context.get("api_calls", 0)
resource_usage["processing_time"] += dp.context.get("processing_time", 0.0)
# Calculate averages if multiple data points
if len(recent_data) > 0:
resource_usage["processing_time"] = resource_usage["processing_time"] / len(recent_data)
except Exception as e:
logger.error(f"Error calculating resource usage for agent {agent_id}: {e}")
return resource_usage
async def analyze_performance_trends(self, agent_id: str, period_hours: int = 24) -> List[PerformanceTrend]:
"""Analyze performance trends for an agent"""
try:
cutoff_time = datetime.utcnow().timestamp() - (period_hours * 60 * 60)
agent_data = [
dp for dp in self.performance_data[agent_id]
if datetime.fromisoformat(dp.timestamp).timestamp() > cutoff_time
]
if len(agent_data) < 5: # Need at least 5 data points for trend analysis
return []
trends = []
# Analyze trends for each metric type
for metric_type in PerformanceMetric:
metric_data = [dp for dp in agent_data if dp.metric_type == metric_type]
if len(metric_data) < 3: # Need at least 3 points for trend
continue
# Sort by timestamp
metric_data.sort(key=lambda x: x.timestamp)
# Calculate trend
trend_result = self._calculate_trend(metric_data)
if trend_result:
trend = PerformanceTrend(
metric_type=metric_type,
trend_direction=trend_result["direction"],
trend_strength=trend_result["strength"],
change_rate=trend_result["change_rate"],
confidence=trend_result["confidence"],
period_start=metric_data[0].timestamp,
period_end=metric_data[-1].timestamp
)
trends.append(trend)
logger.info(f"Analyzed performance trends for agent {agent_id}: found {len(trends)} trends")
return trends
except Exception as e:
logger.error(f"Error analyzing performance trends for agent {agent_id}: {e}")
return []
def _calculate_trend(self, data_points: List[PerformanceDataPoint]) -> Optional[Dict[str, Any]]:
"""Calculate trend from performance data points"""
try:
if len(data_points) < 3:
return None
# Extract values and timestamps
values = [dp.value for dp in data_points]
timestamps = [datetime.fromisoformat(dp.timestamp).timestamp() for dp in data_points]
# Simple linear trend calculation
n = len(values)
sum_x = sum(timestamps)
sum_y = sum(values)
sum_xy = sum(x * y for x, y in zip(timestamps, values))
sum_x2 = sum(x * x for x in timestamps)
# Calculate slope and intercept
slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x)
intercept = (sum_y - slope * sum_x) / n
# Calculate correlation coefficient (confidence)
mean_y = sum_y / n
ss_tot = sum((y - mean_y) ** 2 for y in values)
ss_res = sum((y - (slope * x + intercept)) ** 2 for x, y in zip(timestamps, values))
r_squared = 1 - (ss_res / ss_tot) if ss_tot > 0 else 0
# Determine trend direction
if abs(slope) < 0.001: # Nearly flat
direction = "stable"
strength = 0.0
elif slope > 0:
direction = "improving"
strength = min(1.0, abs(slope) * 100) # Scale slope to 0-1
else:
direction = "declining"
strength = min(1.0, abs(slope) * 100)
# Calculate change rate (percentage change per hour)
time_span = timestamps[-1] - timestamps[0]
if time_span > 0:
change_rate = (slope * 3600) / (values[0] if values[0] != 0 else 1) * 100 # Per hour
else:
change_rate = 0.0
return {
"direction": direction,
"strength": strength,
"change_rate": change_rate,
"confidence": r_squared
}
except Exception as e:
logger.error(f"Error calculating trend: {e}")
return None
async def generate_optimization_recommendations(self, agent_id: str) -> List[OptimizationRecommendation]:
"""Generate optimization recommendations for an agent"""
try:
recommendations = []
# Get current snapshot
snapshot = self.agent_snapshots.get(agent_id)
if not snapshot:
return []
# Get performance trends
trends = await self.analyze_performance_trends(agent_id)
# Generate recommendations based on performance analysis
# 1. Success rate recommendations
if snapshot.success_rate < self.performance_targets["success_rate"]:
recommendation = OptimizationRecommendation(
recommendation_id=f"success_rate_{agent_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
agent_id=agent_id,
user_id=self.user_id,
recommendation_type="success_rate_improvement",
priority="high" if snapshot.success_rate < self.alert_thresholds["success_rate"] else "medium",
description=f"Agent success rate is {snapshot.success_rate:.1%}, target is {self.performance_targets['success_rate']:.1%}",
expected_impact=self.performance_targets["success_rate"] - snapshot.success_rate,
implementation_steps=[
"Analyze recent failed actions to identify patterns",
"Review error logs for common failure causes",
"Update agent parameters or logic to address identified issues",
"Test improvements with small batch of actions",
"Monitor success rate improvement over time"
],
estimated_effort="medium"
)
recommendations.append(recommendation)
# 2. Response time recommendations
if snapshot.average_response_time > self.performance_targets["response_time"]:
recommendation = OptimizationRecommendation(
recommendation_id=f"response_time_{agent_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
agent_id=agent_id,
user_id=self.user_id,
recommendation_type="response_time_optimization",
priority="high" if snapshot.average_response_time > self.alert_thresholds["response_time"] else "medium",
description=f"Agent average response time is {snapshot.average_response_time:.1f}s, target is {self.performance_targets['response_time']:.1f}s",
expected_impact=(self.performance_targets["response_time"] - snapshot.average_response_time) / snapshot.average_response_time,
implementation_steps=[
"Profile agent execution to identify bottlenecks",
"Optimize API calls and external service interactions",
"Implement caching for frequently accessed data",
"Review and optimize agent logic and decision-making",
"Monitor response time improvement"
],
estimated_effort="high"
)
recommendations.append(recommendation)
# 3. Efficiency score recommendations
if snapshot.efficiency_score < self.performance_targets["efficiency_score"]:
recommendation = OptimizationRecommendation(
recommendation_id=f"efficiency_{agent_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
agent_id=agent_id,
user_id=self.user_id,
recommendation_type="efficiency_improvement",
priority="high" if snapshot.efficiency_score < self.alert_thresholds["efficiency_score"] else "medium",
description=f"Agent efficiency score is {snapshot.efficiency_score:.2f}, target is {self.performance_targets['efficiency_score']:.2f}",
expected_impact=self.performance_targets["efficiency_score"] - snapshot.efficiency_score,
implementation_steps=[
"Analyze agent decision-making patterns",
"Identify redundant or unnecessary operations",
"Optimize agent parameters and thresholds",
"Implement better error handling and recovery",
"Monitor efficiency score improvement"
],
estimated_effort="medium"
)
recommendations.append(recommendation)
# 4. Market impact recommendations
if snapshot.market_impact_score < self.performance_targets["market_impact"]:
recommendation = OptimizationRecommendation(
recommendation_id=f"market_impact_{agent_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
agent_id=agent_id,
user_id=self.user_id,
recommendation_type="market_impact_enhancement",
priority="medium",
description=f"Agent market impact score is {snapshot.market_impact_score:.2f}, target is {self.performance_targets['market_impact']:.2f}",
expected_impact=self.performance_targets["market_impact"] - snapshot.market_impact_score,
implementation_steps=[
"Analyze market signal detection accuracy",
"Improve market trend analysis and prediction",
"Enhance competitive intelligence gathering",
"Optimize timing and execution of market actions",
"Monitor market impact score improvement"
],
estimated_effort="high"
)
recommendations.append(recommendation)
# 5. Trend-based recommendations
for trend in trends:
if trend.trend_strength > 0.7 and trend.confidence > 0.8: # Strong trend with high confidence
if trend.trend_direction == "declining":
recommendation = OptimizationRecommendation(
recommendation_id=f"trend_{trend.metric_type.value}_{agent_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
agent_id=agent_id,
user_id=self.user_id,
recommendation_type="trend_reversal",
priority="high" if trend.trend_strength > 0.8 else "medium",
description=f"Strong declining trend detected in {trend.metric_type.value}: {trend.change_rate:.1f}% change per hour",
expected_impact=0.3, # Estimate 30% improvement potential
implementation_steps=[
f"Investigate causes of declining {trend.metric_type.value}",
"Identify specific factors contributing to the trend",
"Implement corrective measures based on findings",
"Monitor trend reversal over time",
"Adjust approach if trend continues"
],
estimated_effort="medium"
)
recommendations.append(recommendation)
# Sort by priority and expected impact
recommendations.sort(key=lambda x: (self._priority_weight(x.priority), x.expected_impact), reverse=True)
# Keep only top 10 recommendations
recommendations = recommendations[:10]
# Store recommendations
self.recommendations.extend(recommendations)
# Keep only recent recommendations (last 50)
if len(self.recommendations) > 50:
self.recommendations = self.recommendations[-50:]
logger.info(f"Generated {len(recommendations)} optimization recommendations for agent {agent_id}")
return recommendations
except Exception as e:
logger.error(f"Error generating optimization recommendations for agent {agent_id}: {e}")
return []
def _priority_weight(self, priority: str) -> int:
"""Convert priority to numeric weight for sorting"""
priority_weights = {
"high": 3,
"medium": 2,
"low": 1
}
return priority_weights.get(priority, 0)
async def get_performance_alerts(self, agent_id: str) -> List[Dict[str, Any]]:
"""Get performance alerts for an agent"""
alerts = []
try:
snapshot = self.agent_snapshots.get(agent_id)
if not snapshot:
return []
# Check success rate alert
if snapshot.success_rate < self.alert_thresholds["success_rate"]:
alerts.append({
"type": "performance_alert",
"metric": "success_rate",
"current_value": snapshot.success_rate,
"threshold": self.alert_thresholds["success_rate"],
"target": self.performance_targets["success_rate"],
"severity": "high" if snapshot.success_rate < 0.5 else "medium",
"message": f"Agent success rate ({snapshot.success_rate:.1%}) is below alert threshold ({self.alert_thresholds['success_rate']:.1%})",
"timestamp": datetime.utcnow().isoformat()
})
# Check response time alert
if snapshot.average_response_time > self.alert_thresholds["response_time"]:
alerts.append({
"type": "performance_alert",
"metric": "response_time",
"current_value": snapshot.average_response_time,
"threshold": self.alert_thresholds["response_time"],
"target": self.performance_targets["response_time"],
"severity": "high" if snapshot.average_response_time > 120 else "medium",
"message": f"Agent response time ({snapshot.average_response_time:.1f}s) exceeds alert threshold ({self.alert_thresholds['response_time']:.1f}s)",
"timestamp": datetime.utcnow().isoformat()
})
# Check efficiency score alert
if snapshot.efficiency_score < self.alert_thresholds["efficiency_score"]:
alerts.append({
"type": "performance_alert",
"metric": "efficiency_score",
"current_value": snapshot.efficiency_score,
"threshold": self.alert_thresholds["efficiency_score"],
"target": self.performance_targets["efficiency_score"],
"severity": "high" if snapshot.efficiency_score < 0.3 else "medium",
"message": f"Agent efficiency score ({snapshot.efficiency_score:.2f}) is below alert threshold ({self.alert_thresholds['efficiency_score']:.2f})",
"timestamp": datetime.utcnow().isoformat()
})
# Check market impact alert
if snapshot.market_impact_score < self.alert_thresholds["market_impact"]:
alerts.append({
"type": "performance_alert",
"metric": "market_impact",
"current_value": snapshot.market_impact_score,
"threshold": self.alert_thresholds["market_impact"],
"target": self.performance_targets["market_impact"],
"severity": "medium",
"message": f"Agent market impact score ({snapshot.market_impact_score:.2f}) is below alert threshold ({self.alert_thresholds['market_impact']:.2f})",
"timestamp": datetime.utcnow().isoformat()
})
return alerts
except Exception as e:
logger.error(f"Error getting performance alerts for agent {agent_id}: {e}")
return []
async def get_performance_summary(self, agent_id: str) -> Dict[str, Any]:
"""Get comprehensive performance summary for an agent"""
try:
snapshot = self.agent_snapshots.get(agent_id)
if not snapshot:
return {}
# Get trends
trends = await self.analyze_performance_trends(agent_id)
# Get recommendations
recommendations = await self.generate_optimization_recommendations(agent_id)
# Get alerts
alerts = await self.get_performance_alerts(agent_id)
# Calculate overall health score
health_score = self._calculate_health_score(snapshot)
return {
"agent_id": agent_id,
"user_id": self.user_id,
"timestamp": datetime.utcnow().isoformat(),
"overall_health": health_score,
"current_performance": asdict(snapshot),
"performance_trends": [asdict(trend) for trend in trends],
"optimization_recommendations": [asdict(rec) for rec in recommendations],
"performance_alerts": alerts,
"performance_targets": self.performance_targets,
"alert_thresholds": self.alert_thresholds
}
except Exception as e:
logger.error(f"Error getting performance summary for agent {agent_id}: {e}")
return {} return {}
aggregated = defaultdict(list) def _calculate_health_score(self, snapshot: AgentPerformanceSnapshot) -> float:
for m in relevant_metrics: """Calculate overall health score based on key metrics"""
for k, v in m.metrics.items(): try:
aggregated[k].append(v) # Weighted scoring based on key metrics
weights = {
result = { "success_rate": 0.3,
"agent_id": agent_id, "response_time": 0.25,
"period_minutes": time_window_minutes, "efficiency_score": 0.25,
"sample_size": len(relevant_metrics), "market_impact": 0.2
"metrics": {
k: sum(v) / len(v) for k, v in aggregated.items()
} }
scores = {
"success_rate": min(1.0, snapshot.success_rate / self.performance_targets["success_rate"]),
"response_time": max(0.0, 1.0 - (snapshot.average_response_time / self.performance_targets["response_time"])),
"efficiency_score": min(1.0, snapshot.efficiency_score / self.performance_targets["efficiency_score"]),
"market_impact": min(1.0, snapshot.market_impact_score / self.performance_targets["market_impact"])
}
# Calculate weighted health score
health_score = sum(scores[metric] * weights[metric] for metric in weights.keys())
return round(health_score, 2)
except Exception as e:
logger.error(f"Error calculating health score: {e}")
return 0.0
def get_all_agents_performance(self) -> List[Dict[str, Any]]:
"""Get performance summary for all agents"""
all_performance = []
for agent_id, snapshot in self.agent_snapshots.items():
performance_summary = {
"agent_id": agent_id,
"user_id": self.user_id,
"status": snapshot.status.value,
"success_rate": snapshot.success_rate,
"efficiency_score": snapshot.efficiency_score,
"response_time": snapshot.average_response_time,
"market_impact": snapshot.market_impact_score,
"total_actions": snapshot.total_actions,
"last_action": snapshot.last_action_at,
"health_score": self._calculate_health_score(snapshot)
}
all_performance.append(performance_summary)
return all_performance
# Service class for performance monitoring
class AgentPerformanceService:
"""Service class for agent performance monitoring operations"""
def __init__(self):
self.monitors: Dict[str, AgentPerformanceMonitor] = {}
self.global_performance_history: deque = deque(maxlen=5000) # Global history
async def get_monitor(self, user_id: str) -> AgentPerformanceMonitor:
"""Get or create a performance monitor for a user"""
if user_id not in self.monitors:
self.monitors[user_id] = AgentPerformanceMonitor(user_id)
return self.monitors[user_id]
async def record_agent_performance(self, user_id: str, agent_id: str, metric_type: PerformanceMetric, value: float, context: Dict[str, Any] = None) -> bool:
"""Record performance data for an agent"""
monitor = await self.get_monitor(user_id)
success = await monitor.record_performance_data(agent_id, metric_type, value, context)
if success:
# Also record in global history
data_point = PerformanceDataPoint(
timestamp=datetime.utcnow().isoformat(),
metric_type=metric_type,
value=value,
context=context or {},
agent_id=agent_id,
user_id=user_id
)
self.global_performance_history.append(data_point)
return success
async def update_agent_performance_snapshot(self, user_id: str, agent_id: str, status: AgentStatus, action_result: Dict[str, Any] = None) -> AgentPerformanceSnapshot:
"""Update performance snapshot for an agent"""
monitor = await self.get_monitor(user_id)
return await monitor.update_agent_snapshot(agent_id, status, action_result)
async def get_agent_performance_summary(self, user_id: str, agent_id: str) -> Dict[str, Any]:
"""Get comprehensive performance summary for an agent"""
monitor = await self.get_monitor(user_id)
return await monitor.get_performance_summary(agent_id)
async def get_all_agents_performance_summary(self, user_id: str) -> List[Dict[str, Any]]:
"""Get performance summary for all agents for a user"""
monitor = await self.get_monitor(user_id)
return monitor.get_all_agents_performance()
async def get_global_performance_stats(self) -> Dict[str, Any]:
"""Get global performance statistics across all users and agents"""
if not self.global_performance_history:
return {}
# Calculate global statistics
total_actions = len([dp for dp in self.global_performance_history if dp.metric_type == PerformanceMetric.SUCCESS_RATE])
successful_actions = len([dp for dp in self.global_performance_history if dp.metric_type == PerformanceMetric.SUCCESS_RATE and dp.value > 0.5])
response_times = [dp.value for dp in self.global_performance_history if dp.metric_type == PerformanceMetric.RESPONSE_TIME]
avg_response_time = sum(response_times) / len(response_times) if response_times else 0.0
efficiency_scores = [dp.value for dp in self.global_performance_history if dp.metric_type == PerformanceMetric.EFFICIENCY_SCORE]
avg_efficiency = sum(efficiency_scores) / len(efficiency_scores) if efficiency_scores else 0.0
unique_users = len(set(dp.user_id for dp in self.global_performance_history))
unique_agents = len(set(dp.agent_id for dp in self.global_performance_history))
return {
"total_actions": total_actions,
"successful_actions": successful_actions,
"overall_success_rate": successful_actions / total_actions if total_actions > 0 else 0.0,
"average_response_time": avg_response_time,
"average_efficiency_score": avg_efficiency,
"unique_users": unique_users,
"unique_agents": unique_agents,
"total_data_points": len(self.global_performance_history),
"timestamp": datetime.utcnow().isoformat()
} }
return result # Global service instance
performance_service = AgentPerformanceService()
async def _check_thresholds(self, agent_id: str, metric_type: PerformanceMetric, value: float): # Convenience functions for external use
"""Check if metric violates thresholds""" async def record_agent_performance(user_id: str, agent_id: str, metric_type: PerformanceMetric, value: float, context: Dict[str, Any] = None) -> bool:
threshold = self.alert_thresholds.get(metric_type) """Record performance data for an agent"""
if not threshold: return await performance_service.record_agent_performance(user_id, agent_id, metric_type, value, context)
return
is_violation = False async def update_agent_performance_snapshot(user_id: str, agent_id: str, status: AgentStatus, action_result: Dict[str, Any] = None) -> AgentPerformanceSnapshot:
if metric_type in [PerformanceMetric.SUCCESS_RATE, PerformanceMetric.GOAL_COMPLETION_RATE]: """Update performance snapshot for an agent"""
if value < threshold: return await performance_service.update_agent_performance_snapshot(user_id, agent_id, status, action_result)
is_violation = True
elif value > threshold:
is_violation = True
if is_violation: async def get_agent_performance_summary(user_id: str, agent_id: str) -> Dict[str, Any]:
logger.warning( """Get comprehensive performance summary for an agent"""
f"Performance alert for agent {agent_id}: " return await performance_service.get_agent_performance_summary(user_id, agent_id)
f"{metric_type.value} = {value} (Threshold: {threshold})"
)
# Trigger alert notification (impl via notification service)
# Singleton instance async def get_all_agents_performance_summary(user_id: str) -> List[Dict[str, Any]]:
performance_monitor = PerformanceMonitor() """Get performance summary for all agents for a user"""
AgentPerformanceMonitor = PerformanceMonitor return await performance_service.get_all_agents_performance_summary(user_id)
performance_service = performance_monitor

View File

@@ -0,0 +1,470 @@
import React, { useEffect, useState } from 'react';
import {
Box,
Card,
CardContent,
Typography,
Grid,
LinearProgress,
Chip,
IconButton,
Tooltip,
Alert,
Skeleton,
Button
} from '@mui/material';
import {
TrendingUp as TrendingUpIcon,
TrendingDown as TrendingDownIcon,
Warning as WarningIcon,
CheckCircle as CheckCircleIcon,
Refresh as RefreshIcon,
Info as InfoIcon,
Speed as SpeedIcon,
Assessment as AssessmentIcon,
Group as GroupIcon,
Lightbulb as LightbulbIcon
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { GlassCard, ShimmerHeader } from '../../shared/styled';
import { useSemanticDashboardStore } from '../../../stores/semanticDashboardStore';
import { SemanticHealthMetric, CompetitorSemanticSnapshot, ContentSemanticInsight } from '../../../api/semanticDashboard';
interface SemanticDashboardProps {
className?: string;
}
const SemanticDashboard: React.FC<SemanticDashboardProps> = ({ className }) => {
const {
semanticHealth,
competitorSnapshots,
contentInsights,
loading,
error,
lastUpdated,
monitoringStatus,
fetchSemanticHealth,
fetchCompetitorSnapshots,
fetchContentInsights,
fetchAllSemanticData,
refreshSemanticAnalysis,
getHealthStatusColor,
getInsightTypeColor,
getConfidenceColor
} = useSemanticDashboardStore();
const [refreshing, setRefreshing] = useState(false);
// Fetch semantic data on component mount
useEffect(() => {
fetchAllSemanticData();
}, []);
// Auto-refresh every 24 hours (86400000ms)
useEffect(() => {
const interval = setInterval(() => {
if (monitoringStatus === 'active') {
fetchAllSemanticData();
}
}, 86400000); // 24 hours
return () => clearInterval(interval);
}, [monitoringStatus, fetchAllSemanticData]);
const handleRefresh = async () => {
setRefreshing(true);
try {
await refreshSemanticAnalysis();
} catch (error) {
console.error('Failed to refresh semantic analysis:', error);
} finally {
setRefreshing(false);
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'healthy':
return <CheckCircleIcon sx={{ color: '#4CAF50' }} />;
case 'warning':
return <WarningIcon sx={{ color: '#FF9800' }} />;
case 'critical':
return <WarningIcon sx={{ color: '#F44336' }} />;
default:
return <InfoIcon sx={{ color: '#9E9E9E' }} />;
}
};
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'up':
return <TrendingUpIcon sx={{ color: '#4CAF50' }} />;
case 'down':
return <TrendingDownIcon sx={{ color: '#F44336' }} />;
default:
return <AssessmentIcon sx={{ color: '#9E9E9E' }} />;
}
};
const formatLastUpdated = (timestamp: string | null) => {
if (!timestamp) return 'Never';
const date = new Date(timestamp);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
if (diffHours < 1) return 'Just now';
if (diffHours < 24) return `${diffHours}h ago`;
return `${Math.floor(diffHours / 24)}d ago`;
};
if (error) {
return (
<GlassCard className={className}>
<CardContent>
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
<Button
variant="outlined"
onClick={fetchAllSemanticData}
disabled={loading}
startIcon={<RefreshIcon />}
>
Retry
</Button>
</CardContent>
</GlassCard>
);
}
return (
<Box className={className}>
{/* Header */}
<Box display="flex" alignItems="center" justifyContent="space-between" mb={3}>
<Box display="flex" alignItems="center" gap={1}>
<AssessmentIcon sx={{ color: '#64B5F6', fontSize: 28 }} />
<Typography variant="h5" component="h2" sx={{ color: 'white', fontWeight: 600 }}>
Semantic Intelligence
</Typography>
<Chip
label={monitoringStatus === 'active' ? 'Active' : 'Inactive'}
color={monitoringStatus === 'active' ? 'success' : 'default'}
size="small"
sx={{ ml: 1 }}
/>
</Box>
<Box display="flex" alignItems="center" gap={1}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Updated: {formatLastUpdated(lastUpdated)}
</Typography>
<Tooltip title="Refresh semantic analysis">
<IconButton
onClick={handleRefresh}
disabled={loading || refreshing}
sx={{ color: 'rgba(255,255,255,0.7)' }}
>
<RefreshIcon />
</IconButton>
</Tooltip>
</Box>
</Box>
{/* Semantic Health Card */}
<GlassCard sx={{ mb: 3 }}>
<ShimmerHeader />
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<SpeedIcon sx={{ color: '#64B5F6', mr: 1 }} />
<Typography variant="h6" sx={{ color: 'white', fontWeight: 600 }}>
Semantic Health
</Typography>
</Box>
{loading && !semanticHealth ? (
<Box>
<Skeleton variant="rectangular" height={60} sx={{ mb: 2, borderRadius: 2 }} />
<Skeleton variant="text" width="80%" />
<Skeleton variant="text" width="60%" />
</Box>
) : semanticHealth ? (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Box display="flex" alignItems="center" mb={2}>
{getStatusIcon(semanticHealth.status)}
<Box ml={2} flex={1}>
<Typography variant="h6" sx={{ color: 'white' }}>
{semanticHealth.metric_name.replace(/_/g, ' ').toUpperCase()}
</Typography>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)' }}>
{semanticHealth.description}
</Typography>
</Box>
<Box textAlign="right">
<Typography variant="h4" sx={{ color: 'white', fontWeight: 700 }}>
{Math.round(semanticHealth.value * 100)}%
</Typography>
<LinearProgress
variant="determinate"
value={semanticHealth.value * 100}
sx={{
width: 100,
height: 6,
borderRadius: 3,
backgroundColor: 'rgba(255,255,255,0.2)',
'& .MuiLinearProgress-bar': {
backgroundColor: getHealthStatusColor(semanticHealth.status),
borderRadius: 3
}
}}
/>
</Box>
</Box>
{semanticHealth.recommendations.length > 0 && (
<Box>
<Typography variant="subtitle2" sx={{ color: 'rgba(255,255,255,0.8)', mb: 1 }}>
Recommendations:
</Typography>
{semanticHealth.recommendations.map((rec, index) => (
<Typography key={index} variant="body2" sx={{ color: 'rgba(255,255,255,0.7)', mb: 0.5 }}>
{rec}
</Typography>
))}
</Box>
)}
</motion.div>
) : (
<Typography sx={{ color: 'rgba(255,255,255,0.7)' }}>
No semantic health data available
</Typography>
)}
</CardContent>
</GlassCard>
{/* Competitor Semantic Analysis */}
<GlassCard sx={{ mb: 3 }}>
<ShimmerHeader />
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<GroupIcon sx={{ color: '#81C784', mr: 1 }} />
<Typography variant="h6" sx={{ color: 'white', fontWeight: 600 }}>
Competitor Semantic Positioning
</Typography>
</Box>
{loading && competitorSnapshots.length === 0 ? (
<Grid container spacing={2}>
{[1, 2, 3].map((i) => (
<Grid item xs={12} md={6} key={i}>
<Skeleton variant="rectangular" height={120} sx={{ borderRadius: 2 }} />
</Grid>
))}
</Grid>
) : competitorSnapshots.length > 0 ? (
<Grid container spacing={2}>
<AnimatePresence>
{competitorSnapshots.map((competitor, index) => (
<Grid item xs={12} md={6} key={competitor.competitor_id}>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Card
sx={{
background: 'rgba(255,255,255,0.05)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 2
}}
>
<CardContent>
<Typography variant="h6" sx={{ color: 'white', mb: 1 }}>
{competitor.competitor_name}
</Typography>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={1}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Semantic Overlap
</Typography>
<Typography variant="h6" sx={{ color: 'white' }}>
{Math.round(competitor.semantic_overlap * 100)}%
</Typography>
</Box>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={1}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Authority Score
</Typography>
<Typography variant="h6" sx={{ color: 'white' }}>
{Math.round(competitor.authority_score * 100)}
</Typography>
</Box>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Content Volume
</Typography>
<Typography variant="body2" sx={{ color: 'white' }}>
{competitor.content_volume} pages
</Typography>
</Box>
{competitor.trending_topics.length > 0 && (
<Box mt={2}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)', mb: 1, display: 'block' }}>
Trending Topics:
</Typography>
<Box display="flex" flexWrap="wrap" gap={0.5}>
{competitor.trending_topics.slice(0, 3).map((topic, idx) => (
<Chip
key={idx}
label={topic}
size="small"
sx={{
backgroundColor: 'rgba(255,255,255,0.1)',
color: 'white',
fontSize: '0.7rem'
}}
/>
))}
</Box>
</Box>
)}
</CardContent>
</Card>
</motion.div>
</Grid>
))}
</AnimatePresence>
</Grid>
) : (
<Typography sx={{ color: 'rgba(255,255,255,0.7)' }}>
No competitor semantic data available
</Typography>
)}
</CardContent>
</GlassCard>
{/* Content Insights */}
<GlassCard>
<ShimmerHeader />
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<LightbulbIcon sx={{ color: '#FFD54F', mr: 1 }} />
<Typography variant="h6" sx={{ color: 'white', fontWeight: 600 }}>
Content Intelligence Insights
</Typography>
</Box>
{loading && contentInsights.length === 0 ? (
<Box>
{[1, 2, 3].map((i) => (
<Box key={i} mb={2}>
<Skeleton variant="rectangular" height={80} sx={{ borderRadius: 2, mb: 1 }} />
</Box>
))}
</Box>
) : contentInsights.length > 0 ? (
<AnimatePresence>
{contentInsights.map((insight, index) => (
<motion.div
key={insight.insight_id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Card
sx={{
background: 'rgba(255,255,255,0.05)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 2,
mb: 2
}}
>
<CardContent>
<Box display="flex" alignItems="center" justifyContent="space-between" mb={1}>
<Box display="flex" alignItems="center" gap={1}>
<Chip
label={insight.insight_type.toUpperCase()}
size="small"
sx={{
backgroundColor: getInsightTypeColor(insight.insight_type),
color: 'white',
fontWeight: 600,
fontSize: '0.7rem'
}}
/>
<Typography variant="subtitle2" sx={{ color: 'white', fontWeight: 600 }}>
{insight.title}
</Typography>
</Box>
<Box display="flex" alignItems="center" gap={1}>
<Tooltip title={`Confidence: ${Math.round(insight.confidence_score * 100)}%`}>
<Box>
<LinearProgress
variant="determinate"
value={insight.confidence_score * 100}
sx={{
width: 60,
height: 4,
borderRadius: 2,
backgroundColor: 'rgba(255,255,255,0.2)',
'& .MuiLinearProgress-bar': {
backgroundColor: getConfidenceColor(insight.confidence_score),
borderRadius: 2
}
}}
/>
</Box>
</Tooltip>
</Box>
</Box>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.8)', mb: 1 }}>
{insight.description}
</Typography>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Impact Score: {Math.round(insight.impact_score * 100)}/100
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Expires: {new Date(insight.expires_at).toLocaleDateString()}
</Typography>
</Box>
{insight.suggested_actions.length > 0 && (
<Box mt={1}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)', mb: 0.5, display: 'block' }}>
Suggested Actions:
</Typography>
{insight.suggested_actions.map((action, idx) => (
<Typography key={idx} variant="caption" sx={{ color: 'rgba(255,255,255,0.6)', display: 'block' }}>
{action}
</Typography>
))}
</Box>
)}
</CardContent>
</Card>
</motion.div>
))}
</AnimatePresence>
) : (
<Typography sx={{ color: 'rgba(255,255,255,0.7)' }}>
No content insights available
</Typography>
)}
</CardContent>
</GlassCard>
</Box>
);
};
export default SemanticDashboard;