Spaces:
Running
Running
""" | |
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.<br>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.<br>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='<b>Query:</b> %{text}<br><b>Time:</b> %{y:.0f}ms<extra></extra>', | |
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.<br>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='<b>%{label}</b><br>Time: %{value:.0f}ms<br>Percentage: %{percent}<extra></extra>' | |
)]) | |
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.<br>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='<b>%{x}</b><br>Health: %{y}%<extra></extra>' | |
)) | |
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() |