Spaces:
Sleeping
Sleeping
Commit
Β·
501015f
1
Parent(s):
5ad6209
everything is is working perfectly
Browse files- app.py +151 -40
- drift_detector.sqlite3 +0 -0
app.py
CHANGED
@@ -574,9 +574,9 @@ def calculate_drift(dropdown_value):
|
|
574 |
|
575 |
|
576 |
def create_drift_chart(drift_history):
|
577 |
-
"""Create drift chart from actual data"""
|
578 |
try:
|
579 |
-
if not drift_history:
|
580 |
# Empty chart if no data
|
581 |
fig = go.Figure()
|
582 |
fig.add_annotation(
|
@@ -589,49 +589,99 @@ def create_drift_chart(drift_history):
|
|
589 |
fig.update_layout(
|
590 |
title='Model Drift Over Time - No Data',
|
591 |
template='plotly_white',
|
592 |
-
height=400
|
|
|
|
|
593 |
)
|
594 |
return fig
|
595 |
|
|
|
|
|
596 |
# Extract dates and scores from actual data
|
597 |
dates = []
|
598 |
scores = []
|
599 |
|
600 |
-
for entry in drift_history:
|
|
|
|
|
601 |
# Handle different date formats
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
614 |
else:
|
615 |
-
|
|
|
|
|
|
|
|
|
|
|
616 |
|
617 |
-
# Handle drift score
|
618 |
-
score = entry.get("drift_score", 0)
|
619 |
if isinstance(score, str):
|
620 |
try:
|
621 |
score = float(score)
|
622 |
-
except:
|
|
|
623 |
score = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
624 |
scores.append(score)
|
|
|
|
|
|
|
625 |
|
|
|
|
|
|
|
|
|
626 |
fig = go.Figure()
|
|
|
|
|
627 |
fig.add_trace(go.Scatter(
|
628 |
x=dates,
|
629 |
y=scores,
|
630 |
mode='lines+markers',
|
631 |
name='Drift Score',
|
632 |
line=dict(color='#ff6b6b', width=3),
|
633 |
-
marker=dict(
|
634 |
-
|
|
|
|
|
|
|
|
|
|
|
635 |
))
|
636 |
|
637 |
# Add threshold line at 50%
|
@@ -639,55 +689,110 @@ def create_drift_chart(drift_history):
|
|
639 |
y=50,
|
640 |
line_dash="dash",
|
641 |
line_color="orange",
|
642 |
-
|
|
|
|
|
643 |
)
|
644 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
645 |
fig.update_layout(
|
646 |
-
title=f'Model Drift Over Time ({len(drift_history)}
|
647 |
xaxis_title='Date',
|
648 |
yaxis_title='Drift Score (%)',
|
649 |
template='plotly_white',
|
650 |
-
height=
|
651 |
showlegend=True,
|
652 |
-
yaxis=dict(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
653 |
)
|
654 |
|
|
|
|
|
|
|
|
|
655 |
return fig
|
656 |
|
657 |
except Exception as e:
|
658 |
print(f"β Error creating drift chart: {e}")
|
659 |
-
|
|
|
|
|
660 |
fig = go.Figure()
|
661 |
fig.add_annotation(
|
662 |
-
text=f"Error creating chart
|
663 |
xref="paper", yref="paper",
|
664 |
x=0.5, y=0.5,
|
665 |
showarrow=False,
|
666 |
-
font=dict(size=14, color="red")
|
|
|
667 |
)
|
668 |
fig.update_layout(
|
669 |
title='Error Creating Drift Chart',
|
670 |
template='plotly_white',
|
671 |
-
height=400
|
|
|
|
|
672 |
)
|
673 |
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
674 |
def refresh_drift_history(dropdown_value):
|
675 |
-
"""Refresh drift history
|
676 |
if not dropdown_value:
|
677 |
return [], gr.update(value=None)
|
678 |
|
679 |
try:
|
680 |
model_name = extract_model_name_from_dropdown(dropdown_value, current_model_mapping)
|
|
|
681 |
|
682 |
if not DATABASE_AVAILABLE:
|
683 |
-
#
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
691 |
else:
|
692 |
# Get actual drift history from database
|
693 |
history_result = get_drift_history_handler({"model_name": model_name})
|
@@ -699,14 +804,20 @@ def refresh_drift_history(dropdown_value):
|
|
699 |
history = []
|
700 |
print(f"β οΈ No drift history found for {model_name}")
|
701 |
|
|
|
|
|
|
|
|
|
702 |
chart = create_drift_chart(history)
|
|
|
703 |
return history, chart
|
704 |
|
705 |
except Exception as e:
|
706 |
print(f"β Error refreshing drift history: {e}")
|
|
|
|
|
707 |
return [], gr.update(value=None)
|
708 |
|
709 |
-
|
710 |
def initialize_interface():
|
711 |
"""Initialize interface"""
|
712 |
global current_model_mapping
|
|
|
574 |
|
575 |
|
576 |
def create_drift_chart(drift_history):
|
577 |
+
"""Create drift chart from actual data with improved data handling"""
|
578 |
try:
|
579 |
+
if not drift_history or len(drift_history) == 0:
|
580 |
# Empty chart if no data
|
581 |
fig = go.Figure()
|
582 |
fig.add_annotation(
|
|
|
589 |
fig.update_layout(
|
590 |
title='Model Drift Over Time - No Data',
|
591 |
template='plotly_white',
|
592 |
+
height=400,
|
593 |
+
xaxis_title='Date',
|
594 |
+
yaxis_title='Drift Score (%)'
|
595 |
)
|
596 |
return fig
|
597 |
|
598 |
+
print(f"π DEBUG: Processing {len(drift_history)} drift records")
|
599 |
+
|
600 |
# Extract dates and scores from actual data
|
601 |
dates = []
|
602 |
scores = []
|
603 |
|
604 |
+
for i, entry in enumerate(drift_history):
|
605 |
+
print(f"π DEBUG: Processing entry {i}: {entry}")
|
606 |
+
|
607 |
# Handle different date formats
|
608 |
+
date_value = entry.get("date", entry.get("created_at", entry.get("timestamp", "")))
|
609 |
+
|
610 |
+
if date_value:
|
611 |
+
if isinstance(date_value, str):
|
612 |
+
try:
|
613 |
+
from datetime import datetime
|
614 |
+
# Try different date formats
|
615 |
+
if "T" in date_value:
|
616 |
+
# ISO format with time
|
617 |
+
date_obj = datetime.fromisoformat(date_value.replace("Z", "+00:00"))
|
618 |
+
formatted_date = date_obj.strftime("%Y-%m-%d")
|
619 |
+
elif "-" in date_value and len(date_value) >= 10:
|
620 |
+
# YYYY-MM-DD format
|
621 |
+
date_obj = datetime.strptime(date_value[:10], "%Y-%m-%d")
|
622 |
+
formatted_date = date_obj.strftime("%Y-%m-%d")
|
623 |
+
else:
|
624 |
+
# Use as-is if can't parse
|
625 |
+
formatted_date = str(date_value)[:10]
|
626 |
+
except Exception as date_error:
|
627 |
+
print(f"β οΈ Date parsing error for '{date_value}': {date_error}")
|
628 |
+
formatted_date = f"Entry {i + 1}"
|
629 |
+
else:
|
630 |
+
# Handle datetime objects
|
631 |
+
try:
|
632 |
+
formatted_date = date_value.strftime("%Y-%m-%d")
|
633 |
+
except:
|
634 |
+
formatted_date = str(date_value)
|
635 |
else:
|
636 |
+
formatted_date = f"Entry {i + 1}"
|
637 |
+
|
638 |
+
dates.append(formatted_date)
|
639 |
+
|
640 |
+
# Handle drift score - try multiple possible field names
|
641 |
+
score = entry.get("drift_score", entry.get("score", entry.get("drift", 0)))
|
642 |
|
|
|
|
|
643 |
if isinstance(score, str):
|
644 |
try:
|
645 |
score = float(score)
|
646 |
+
except ValueError:
|
647 |
+
print(f"β οΈ Could not convert score '{score}' to float, using 0")
|
648 |
score = 0
|
649 |
+
elif score is None:
|
650 |
+
score = 0
|
651 |
+
|
652 |
+
# Convert decimal to percentage if needed
|
653 |
+
if isinstance(score, (int, float)):
|
654 |
+
if 0 <= score <= 1:
|
655 |
+
score = score * 100 # Convert decimal to percentage
|
656 |
+
score = max(0, min(100, score)) # Clamp between 0-100
|
657 |
+
else:
|
658 |
+
score = 0
|
659 |
+
|
660 |
scores.append(score)
|
661 |
+
print(f"π DEBUG: Added point - Date: {formatted_date}, Score: {score}")
|
662 |
+
|
663 |
+
print(f"π DEBUG: Final data - Dates: {dates}, Scores: {scores}")
|
664 |
|
665 |
+
if len(dates) == 0 or len(scores) == 0:
|
666 |
+
raise ValueError("No valid data points found")
|
667 |
+
|
668 |
+
# Create the plot
|
669 |
fig = go.Figure()
|
670 |
+
|
671 |
+
# Add the main drift line
|
672 |
fig.add_trace(go.Scatter(
|
673 |
x=dates,
|
674 |
y=scores,
|
675 |
mode='lines+markers',
|
676 |
name='Drift Score',
|
677 |
line=dict(color='#ff6b6b', width=3),
|
678 |
+
marker=dict(
|
679 |
+
size=10,
|
680 |
+
color='#ff6b6b',
|
681 |
+
line=dict(width=2, color='white')
|
682 |
+
),
|
683 |
+
hovertemplate='<b>Date:</b> %{x}<br><b>Drift Score:</b> %{y:.1f}%<extra></extra>',
|
684 |
+
connectgaps=True # Connect points even if there are gaps
|
685 |
))
|
686 |
|
687 |
# Add threshold line at 50%
|
|
|
689 |
y=50,
|
690 |
line_dash="dash",
|
691 |
line_color="orange",
|
692 |
+
line_width=2,
|
693 |
+
annotation_text="Alert Threshold (50%)",
|
694 |
+
annotation_position="bottom right"
|
695 |
)
|
696 |
|
697 |
+
# Add another threshold at 75% for critical level
|
698 |
+
fig.add_hline(
|
699 |
+
y=75,
|
700 |
+
line_dash="dot",
|
701 |
+
line_color="red",
|
702 |
+
line_width=2,
|
703 |
+
annotation_text="Critical Threshold (75%)",
|
704 |
+
annotation_position="top right"
|
705 |
+
)
|
706 |
+
|
707 |
+
# Update layout with better formatting
|
708 |
fig.update_layout(
|
709 |
+
title=f'Model Drift Over Time ({len(drift_history)} data points)',
|
710 |
xaxis_title='Date',
|
711 |
yaxis_title='Drift Score (%)',
|
712 |
template='plotly_white',
|
713 |
+
height=450,
|
714 |
showlegend=True,
|
715 |
+
yaxis=dict(
|
716 |
+
range=[0, 100], # Set Y-axis range from 0 to 100%
|
717 |
+
ticksuffix='%'
|
718 |
+
),
|
719 |
+
xaxis=dict(
|
720 |
+
tickangle=45 if len(dates) > 5 else 0, # Angle labels for many dates
|
721 |
+
type='category' # Treat dates as categories for better spacing
|
722 |
+
),
|
723 |
+
hovermode='x unified', # Better hover experience
|
724 |
+
margin=dict(b=100) # More bottom margin for angled labels
|
725 |
)
|
726 |
|
727 |
+
# Add grid for better readability
|
728 |
+
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
|
729 |
+
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
|
730 |
+
|
731 |
return fig
|
732 |
|
733 |
except Exception as e:
|
734 |
print(f"β Error creating drift chart: {e}")
|
735 |
+
print(f"β Drift history data: {drift_history}")
|
736 |
+
|
737 |
+
# Return error chart
|
738 |
fig = go.Figure()
|
739 |
fig.add_annotation(
|
740 |
+
text=f"Error creating chart:\n{str(e)}\n\nCheck console for details",
|
741 |
xref="paper", yref="paper",
|
742 |
x=0.5, y=0.5,
|
743 |
showarrow=False,
|
744 |
+
font=dict(size=14, color="red"),
|
745 |
+
align="center"
|
746 |
)
|
747 |
fig.update_layout(
|
748 |
title='Error Creating Drift Chart',
|
749 |
template='plotly_white',
|
750 |
+
height=400,
|
751 |
+
xaxis_title='Date',
|
752 |
+
yaxis_title='Drift Score (%)'
|
753 |
)
|
754 |
return fig
|
755 |
+
|
756 |
+
|
757 |
+
def debug_drift_data(drift_history):
|
758 |
+
"""Helper function to debug drift history data structure"""
|
759 |
+
print("π DEBUG: Drift History Analysis")
|
760 |
+
print(f"Type: {type(drift_history)}")
|
761 |
+
print(f"Length: {len(drift_history) if drift_history else 0}")
|
762 |
+
|
763 |
+
if drift_history:
|
764 |
+
for i, entry in enumerate(drift_history[:3]): # Show first 3 entries
|
765 |
+
print(f"Entry {i}: {entry}")
|
766 |
+
print(f" Keys: {list(entry.keys()) if isinstance(entry, dict) else 'Not a dict'}")
|
767 |
+
|
768 |
+
return drift_history
|
769 |
+
|
770 |
+
|
771 |
def refresh_drift_history(dropdown_value):
|
772 |
+
"""Refresh drift history with improved debugging"""
|
773 |
if not dropdown_value:
|
774 |
return [], gr.update(value=None)
|
775 |
|
776 |
try:
|
777 |
model_name = extract_model_name_from_dropdown(dropdown_value, current_model_mapping)
|
778 |
+
print(f"π DEBUG: Getting drift history for model: {model_name}")
|
779 |
|
780 |
if not DATABASE_AVAILABLE:
|
781 |
+
# Enhanced mock data for demo mode
|
782 |
+
from datetime import datetime, timedelta
|
783 |
+
base_date = datetime.now() - timedelta(days=10)
|
784 |
+
|
785 |
+
history = []
|
786 |
+
for i in range(6): # Create 6 data points
|
787 |
+
date_obj = base_date + timedelta(days=i * 2)
|
788 |
+
score = 20 + (i * 15) + (i % 2 * 10) # Varied scores: 20, 45, 50, 75, 70, 95
|
789 |
+
history.append({
|
790 |
+
"date": date_obj.strftime("%Y-%m-%d"),
|
791 |
+
"drift_score": min(95, score), # Cap at 95
|
792 |
+
"model_name": model_name
|
793 |
+
})
|
794 |
+
|
795 |
+
print(f"π DEBUG: Generated {len(history)} mock drift records")
|
796 |
else:
|
797 |
# Get actual drift history from database
|
798 |
history_result = get_drift_history_handler({"model_name": model_name})
|
|
|
804 |
history = []
|
805 |
print(f"β οΈ No drift history found for {model_name}")
|
806 |
|
807 |
+
# Debug the data structure
|
808 |
+
history = debug_drift_data(history)
|
809 |
+
|
810 |
+
# Create chart
|
811 |
chart = create_drift_chart(history)
|
812 |
+
|
813 |
return history, chart
|
814 |
|
815 |
except Exception as e:
|
816 |
print(f"β Error refreshing drift history: {e}")
|
817 |
+
import traceback
|
818 |
+
traceback.print_exc()
|
819 |
return [], gr.update(value=None)
|
820 |
|
|
|
821 |
def initialize_interface():
|
822 |
"""Initialize interface"""
|
823 |
global current_model_mapping
|
drift_detector.sqlite3
CHANGED
Binary files a/drift_detector.sqlite3 and b/drift_detector.sqlite3 differ
|
|