"""
Analytics Dashboard for Epic 2 Demo
===================================
Creates interactive Plotly visualizations for real-time performance monitoring
and component health analysis.
"""
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import streamlit as st
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional
import time
from collections import deque
class PerformanceTracker:
"""Tracks performance metrics over time for analytics"""
def __init__(self, max_history: int = 100):
self.max_history = max_history
self.query_history = deque(maxlen=max_history)
self.stage_history = deque(maxlen=max_history)
def add_query(self, query: str, performance: Dict[str, Any]):
"""Add a query performance record"""
timestamp = datetime.now()
record = {
'timestamp': timestamp,
'query': query,
'total_time_ms': performance.get('total_time_ms', 0),
'stages': performance.get('stages', {}),
'component_details': performance.get('component_details', {})
}
self.query_history.append(record)
# Add stage-specific records
for stage_name, stage_data in performance.get('stages', {}).items():
stage_record = {
'timestamp': timestamp,
'query': query,
'stage': stage_name,
'time_ms': stage_data.get('time_ms', 0),
'results': stage_data.get('results', 0)
}
self.stage_history.append(stage_record)
def get_recent_queries(self, limit: int = 10) -> List[Dict]:
"""Get recent query records"""
return list(self.query_history)[-limit:]
def get_stage_performance_df(self) -> pd.DataFrame:
"""Get stage performance as DataFrame"""
if not self.stage_history:
return pd.DataFrame()
return pd.DataFrame(self.stage_history)
def get_query_performance_df(self) -> pd.DataFrame:
"""Get query performance as DataFrame"""
if not self.query_history:
return pd.DataFrame()
return pd.DataFrame(self.query_history)
class AnalyticsDashboard:
"""Main analytics dashboard with interactive charts"""
def __init__(self):
self.tracker = PerformanceTracker()
def add_query_data(self, query: str, performance: Dict[str, Any]):
"""Add query data to tracking"""
self.tracker.add_query(query, performance)
def create_stage_performance_chart(self) -> go.Figure:
"""Create interactive stage performance chart"""
df = self.tracker.get_stage_performance_df()
if df.empty:
# Return empty chart with placeholder
fig = go.Figure()
fig.add_annotation(
text="No performance data available yet.
Run some queries to see analytics!",
xref="paper", yref="paper",
x=0.5, y=0.5, xanchor='center', yanchor='middle',
showarrow=False, font=dict(size=16)
)
fig.update_layout(
title="Stage Performance Over Time",
xaxis_title="Time",
yaxis_title="Duration (ms)",
height=400
)
return fig
# Create interactive line chart
fig = px.line(
df,
x='timestamp',
y='time_ms',
color='stage',
title="Stage Performance Over Time",
labels={'time_ms': 'Duration (ms)', 'timestamp': 'Time'},
hover_data=['query', 'results']
)
# Customize layout
fig.update_layout(
height=400,
hovermode='x unified',
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
)
)
return fig
def create_query_performance_chart(self) -> go.Figure:
"""Create query performance overview chart"""
df = self.tracker.get_query_performance_df()
if df.empty:
fig = go.Figure()
fig.add_annotation(
text="No query data available yet.
Run some queries to see performance trends!",
xref="paper", yref="paper",
x=0.5, y=0.5, xanchor='center', yanchor='middle',
showarrow=False, font=dict(size=16)
)
fig.update_layout(
title="Query Performance Trends",
xaxis_title="Query",
yaxis_title="Total Time (ms)",
height=400
)
return fig
# Create bar chart of recent queries
recent_queries = df.tail(20) # Last 20 queries
fig = go.Figure()
fig.add_trace(go.Bar(
x=list(range(len(recent_queries))),
y=recent_queries['total_time_ms'],
text=[f"{q[:30]}..." if len(q) > 30 else q for q in recent_queries['query']],
textposition='auto',
hovertemplate='Query: %{text}
Time: %{y:.0f}ms',
marker_color='rgba(46, 134, 171, 0.7)'
))
fig.update_layout(
title="Recent Query Performance",
xaxis_title="Query Index",
yaxis_title="Total Time (ms)",
height=400,
showlegend=False
)
return fig
def create_stage_breakdown_chart(self) -> go.Figure:
"""Create stage breakdown pie chart for latest query"""
df = self.tracker.get_stage_performance_df()
if df.empty:
fig = go.Figure()
fig.add_annotation(
text="No stage data available yet.
Run a query to see stage breakdown!",
xref="paper", yref="paper",
x=0.5, y=0.5, xanchor='center', yanchor='middle',
showarrow=False, font=dict(size=16)
)
fig.update_layout(
title="Stage Breakdown (Latest Query)",
height=400
)
return fig
# Get latest query's stage data
latest_timestamp = df['timestamp'].max()
latest_data = df[df['timestamp'] == latest_timestamp]
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=latest_data['stage'],
values=latest_data['time_ms'],
hole=0.3,
textinfo='label+percent',
hovertemplate='%{label}
Time: %{value:.0f}ms
Percentage: %{percent}'
)])
fig.update_layout(
title="Stage Breakdown (Latest Query)",
height=400,
showlegend=True,
legend=dict(
orientation="v",
yanchor="middle",
y=0.5,
xanchor="left",
x=1.01
)
)
return fig
def create_component_health_chart(self) -> go.Figure:
"""Create component health monitoring chart"""
df = self.tracker.get_query_performance_df()
if df.empty:
fig = go.Figure()
fig.add_annotation(
text="No component data available yet.
Run queries to see component health!",
xref="paper", yref="paper",
x=0.5, y=0.5, xanchor='center', yanchor='middle',
showarrow=False, font=dict(size=16)
)
fig.update_layout(
title="Component Health Status",
height=400
)
return fig
# Calculate component health metrics
recent_queries = df.tail(10)
# Mock component health data (in real implementation, this would come from actual metrics)
components = ['Database', 'Retriever', 'Generator', 'Neural Reranker', 'Graph Engine']
health_scores = [95, 98, 97, 93, 96] # Mock scores
# Create gauge-style chart
fig = go.Figure()
colors = ['green' if score >= 95 else 'yellow' if score >= 90 else 'red' for score in health_scores]
fig.add_trace(go.Bar(
x=components,
y=health_scores,
marker_color=colors,
text=[f"{score}%" for score in health_scores],
textposition='auto',
hovertemplate='%{x}
Health: %{y}%'
))
fig.update_layout(
title="Component Health Status",
xaxis_title="Component",
yaxis_title="Health Score (%)",
yaxis=dict(range=[0, 100]),
height=400,
showlegend=False
)
return fig
def create_performance_summary_metrics(self) -> Dict[str, Any]:
"""Create performance summary metrics"""
df = self.tracker.get_query_performance_df()
if df.empty:
return {
'total_queries': 0,
'avg_response_time': 0,
'fastest_query': 0,
'slowest_query': 0,
'success_rate': 0
}
return {
'total_queries': len(df),
'avg_response_time': df['total_time_ms'].mean(),
'fastest_query': df['total_time_ms'].min(),
'slowest_query': df['total_time_ms'].max(),
'success_rate': 100 # Assuming all queries succeed for now
}
def render_dashboard(self):
"""Render the complete analytics dashboard"""
st.header("📊 Real-Time Analytics Dashboard")
# Performance summary metrics
metrics = self.create_performance_summary_metrics()
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Queries", metrics['total_queries'])
with col2:
st.metric("Avg Response Time", f"{metrics['avg_response_time']:.0f}ms")
with col3:
st.metric("Fastest Query", f"{metrics['fastest_query']:.0f}ms")
with col4:
st.metric("Success Rate", f"{metrics['success_rate']:.1f}%")
# Performance charts
col1, col2 = st.columns(2)
with col1:
st.plotly_chart(
self.create_stage_performance_chart(),
use_container_width=True
)
with col2:
st.plotly_chart(
self.create_query_performance_chart(),
use_container_width=True
)
# Additional charts
col1, col2 = st.columns(2)
with col1:
st.plotly_chart(
self.create_stage_breakdown_chart(),
use_container_width=True
)
with col2:
st.plotly_chart(
self.create_component_health_chart(),
use_container_width=True
)
# Query history table
if not self.tracker.query_history:
st.info("No query history available yet. Run some queries to see analytics!")
else:
st.subheader("📈 Recent Query History")
recent_queries = self.tracker.get_recent_queries(10)
history_data = []
for record in recent_queries:
history_data.append({
'Time': record['timestamp'].strftime('%H:%M:%S'),
'Query': record['query'][:50] + '...' if len(record['query']) > 50 else record['query'],
'Response Time (ms)': f"{record['total_time_ms']:.0f}",
'Status': '✅ Success'
})
st.table(pd.DataFrame(history_data))
# Global analytics dashboard instance
analytics_dashboard = AnalyticsDashboard()