Spaces:
Running
Add Run Report Card feature and fix event handler issues
Browse filesFeatures Added:
- Add Logo.png branding asset for report cards
- Implement run report card display in new Report Card tab on Run Detail screen
- Add Download Run Report Card button with PNG export functionality
- Wire up report card generation in both navigation paths (HTML table and DrillDown)
- Enable Download as PNG button for leaderboard summary cards
Bug Fixes:
- Fix DrillDown table event handler to include run_card_html and performance_charts outputs
- Fix all return statements in on_drilldown_select() to include all required outputs
- Fix all return statements in on_html_table_row_click() to include run_card_html
- Fix generate_card() function to return download button visibility update
UI Enhancements:
- Add dynamic chart explanations to Analytics tab accordion
- Copy exact explanation text from MockTraceMind for all 3 chart types
- Add README accordions to all leaderboard tabs (Leaderboard, DrillDown, Trends, Summary Card, AI Insights)
- Make download button visible after generating summary card
|
@@ -20,7 +20,7 @@ from components.analytics_charts import (
|
|
| 20 |
create_speed_accuracy_scatter,
|
| 21 |
create_cost_efficiency_scatter
|
| 22 |
)
|
| 23 |
-
from components.report_cards import generate_leaderboard_summary_card
|
| 24 |
from screens.trace_detail import (
|
| 25 |
create_span_visualization,
|
| 26 |
create_span_table,
|
|
@@ -684,8 +684,12 @@ def update_analytics(viz_type):
|
|
| 684 |
def generate_card(top_n):
|
| 685 |
"""Generate summary card HTML"""
|
| 686 |
df = data_loader.load_leaderboard()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
html = generate_leaderboard_summary_card(df, top_n)
|
| 688 |
-
return html
|
| 689 |
|
| 690 |
|
| 691 |
def generate_insights():
|
|
@@ -743,6 +747,7 @@ def on_html_table_row_click(row_index_str):
|
|
| 743 |
run_detail_screen: gr.update(),
|
| 744 |
run_metadata_html: gr.update(),
|
| 745 |
test_cases_table: gr.update(),
|
|
|
|
| 746 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 747 |
}
|
| 748 |
|
|
@@ -758,6 +763,7 @@ def on_html_table_row_click(row_index_str):
|
|
| 758 |
run_detail_screen: gr.update(),
|
| 759 |
run_metadata_html: gr.update(),
|
| 760 |
test_cases_table: gr.update(),
|
|
|
|
| 761 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 762 |
}
|
| 763 |
|
|
@@ -769,6 +775,7 @@ def on_html_table_row_click(row_index_str):
|
|
| 769 |
run_detail_screen: gr.update(),
|
| 770 |
run_metadata_html: gr.update(),
|
| 771 |
test_cases_table: gr.update(),
|
|
|
|
| 772 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 773 |
}
|
| 774 |
|
|
@@ -796,6 +803,34 @@ def on_html_table_row_click(row_index_str):
|
|
| 796 |
# Generate performance chart
|
| 797 |
perf_chart = create_performance_charts(results_df)
|
| 798 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 799 |
# Format results for display
|
| 800 |
display_df = results_df.copy()
|
| 801 |
|
|
@@ -830,6 +865,7 @@ def on_html_table_row_click(row_index_str):
|
|
| 830 |
run_detail_screen: gr.update(visible=True),
|
| 831 |
run_metadata_html: gr.update(value=metadata_html),
|
| 832 |
test_cases_table: gr.update(value=display_df),
|
|
|
|
| 833 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 834 |
}
|
| 835 |
|
|
@@ -843,6 +879,7 @@ def on_html_table_row_click(row_index_str):
|
|
| 843 |
run_detail_screen: gr.update(visible=False),
|
| 844 |
run_metadata_html: gr.update(),
|
| 845 |
test_cases_table: gr.update(),
|
|
|
|
| 846 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 847 |
}
|
| 848 |
|
|
@@ -867,6 +904,34 @@ def load_run_detail(run_id):
|
|
| 867 |
# Generate performance chart
|
| 868 |
perf_chart = create_performance_charts(results_df)
|
| 869 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
# Format results for display
|
| 871 |
display_df = results_df.copy()
|
| 872 |
|
|
@@ -937,7 +1002,9 @@ def on_drilldown_select(evt: gr.SelectData, df):
|
|
| 937 |
leaderboard_screen: gr.update(visible=True),
|
| 938 |
run_detail_screen: gr.update(visible=False),
|
| 939 |
run_metadata_html: gr.update(value="<h3>No results dataset found</h3>"),
|
| 940 |
-
test_cases_table: gr.update(value=pd.DataFrame())
|
|
|
|
|
|
|
| 941 |
}
|
| 942 |
|
| 943 |
results_df = data_loader.load_results(results_dataset)
|
|
@@ -970,6 +1037,9 @@ def on_drilldown_select(evt: gr.SelectData, df):
|
|
| 970 |
</div>
|
| 971 |
"""
|
| 972 |
|
|
|
|
|
|
|
|
|
|
| 973 |
# Format results for display
|
| 974 |
display_df = results_df.copy()
|
| 975 |
|
|
@@ -1004,7 +1074,8 @@ def on_drilldown_select(evt: gr.SelectData, df):
|
|
| 1004 |
run_detail_screen: gr.update(visible=True),
|
| 1005 |
run_metadata_html: gr.update(value=metadata_html),
|
| 1006 |
test_cases_table: gr.update(value=display_df),
|
| 1007 |
-
performance_charts: gr.update(value=perf_chart)
|
|
|
|
| 1008 |
}
|
| 1009 |
|
| 1010 |
except Exception as e:
|
|
@@ -1018,7 +1089,9 @@ def on_drilldown_select(evt: gr.SelectData, df):
|
|
| 1018 |
leaderboard_screen: gr.update(visible=True), # Stay on leaderboard
|
| 1019 |
run_detail_screen: gr.update(visible=False),
|
| 1020 |
run_metadata_html: gr.update(value="<h3>Error loading run detail</h3>"),
|
| 1021 |
-
test_cases_table: gr.update(value=pd.DataFrame())
|
|
|
|
|
|
|
| 1022 |
}
|
| 1023 |
|
| 1024 |
|
|
@@ -1428,6 +1501,7 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
|
|
| 1428 |
# Navigation
|
| 1429 |
with gr.Row():
|
| 1430 |
back_to_leaderboard_btn = gr.Button("β¬
οΈ Back to Leaderboard", variant="secondary", size="sm")
|
|
|
|
| 1431 |
|
| 1432 |
run_detail_title = gr.Markdown("# π Run Detail")
|
| 1433 |
|
|
@@ -1449,6 +1523,10 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
|
|
| 1449 |
gr.Markdown("*Performance metrics and charts*")
|
| 1450 |
performance_charts = gr.Plot(label="Performance Analysis", show_label=False)
|
| 1451 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1452 |
# Screen 4: Trace Detail with Sub-tabs
|
| 1453 |
with gr.Column(visible=False) as trace_detail_screen:
|
| 1454 |
with gr.Row():
|
|
@@ -1671,7 +1749,13 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
|
|
| 1671 |
generate_card_btn.click(
|
| 1672 |
fn=generate_card,
|
| 1673 |
inputs=[top_n_slider],
|
| 1674 |
-
outputs=[card_preview]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1675 |
)
|
| 1676 |
|
| 1677 |
app.load(
|
|
@@ -1739,7 +1823,7 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
|
|
| 1739 |
leaderboard_table.select(
|
| 1740 |
fn=on_drilldown_select,
|
| 1741 |
inputs=[leaderboard_table], # Pass dataframe to handler (like MockTraceMind)
|
| 1742 |
-
outputs=[leaderboard_screen, run_detail_screen, run_metadata_html, test_cases_table, performance_charts]
|
| 1743 |
)
|
| 1744 |
|
| 1745 |
back_to_leaderboard_btn.click(
|
|
@@ -1777,7 +1861,13 @@ with gr.Blocks(title="TraceMind-AI", theme=theme) as app:
|
|
| 1777 |
selected_row_index.change(
|
| 1778 |
fn=on_html_table_row_click,
|
| 1779 |
inputs=[selected_row_index],
|
| 1780 |
-
outputs=[leaderboard_screen, run_detail_screen, run_metadata_html, test_cases_table, selected_row_index]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1781 |
)
|
| 1782 |
|
| 1783 |
|
|
|
|
| 20 |
create_speed_accuracy_scatter,
|
| 21 |
create_cost_efficiency_scatter
|
| 22 |
)
|
| 23 |
+
from components.report_cards import generate_leaderboard_summary_card, generate_run_report_card, download_card_as_png_js
|
| 24 |
from screens.trace_detail import (
|
| 25 |
create_span_visualization,
|
| 26 |
create_span_table,
|
|
|
|
| 684 |
def generate_card(top_n):
|
| 685 |
"""Generate summary card HTML"""
|
| 686 |
df = data_loader.load_leaderboard()
|
| 687 |
+
|
| 688 |
+
if df is None or df.empty:
|
| 689 |
+
return "<p>No data available</p>", gr.update(visible=False)
|
| 690 |
+
|
| 691 |
html = generate_leaderboard_summary_card(df, top_n)
|
| 692 |
+
return html, gr.update(visible=True)
|
| 693 |
|
| 694 |
|
| 695 |
def generate_insights():
|
|
|
|
| 747 |
run_detail_screen: gr.update(),
|
| 748 |
run_metadata_html: gr.update(),
|
| 749 |
test_cases_table: gr.update(),
|
| 750 |
+
run_card_html: gr.update(),
|
| 751 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 752 |
}
|
| 753 |
|
|
|
|
| 763 |
run_detail_screen: gr.update(),
|
| 764 |
run_metadata_html: gr.update(),
|
| 765 |
test_cases_table: gr.update(),
|
| 766 |
+
run_card_html: gr.update(),
|
| 767 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 768 |
}
|
| 769 |
|
|
|
|
| 775 |
run_detail_screen: gr.update(),
|
| 776 |
run_metadata_html: gr.update(),
|
| 777 |
test_cases_table: gr.update(),
|
| 778 |
+
run_card_html: gr.update(),
|
| 779 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 780 |
}
|
| 781 |
|
|
|
|
| 803 |
# Generate performance chart
|
| 804 |
perf_chart = create_performance_charts(results_df)
|
| 805 |
|
| 806 |
+
# Create metadata HTML
|
| 807 |
+
metadata_html = f"""
|
| 808 |
+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 809 |
+
padding: 20px; border-radius: 10px; color: white; margin-bottom: 20px;">
|
| 810 |
+
<h2 style="margin: 0 0 10px 0;">π Run Detail: {run_data.get('model', 'Unknown')}</h2>
|
| 811 |
+
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; margin-top: 15px;">
|
| 812 |
+
<div>
|
| 813 |
+
<strong>Agent Type:</strong> {run_data.get('agent_type', 'N/A')}<br>
|
| 814 |
+
<strong>Provider:</strong> {run_data.get('provider', 'N/A')}<br>
|
| 815 |
+
<strong>Success Rate:</strong> {run_data.get('success_rate', 0):.1f}%
|
| 816 |
+
</div>
|
| 817 |
+
<div>
|
| 818 |
+
<strong>Total Tests:</strong> {run_data.get('total_tests', 0)}<br>
|
| 819 |
+
<strong>Successful:</strong> {run_data.get('successful_tests', 0)}<br>
|
| 820 |
+
<strong>Failed:</strong> {run_data.get('failed_tests', 0)}
|
| 821 |
+
</div>
|
| 822 |
+
<div>
|
| 823 |
+
<strong>Total Cost:</strong> ${run_data.get('total_cost_usd', 0):.4f}<br>
|
| 824 |
+
<strong>Avg Duration:</strong> {run_data.get('avg_duration_ms', 0):.0f}ms<br>
|
| 825 |
+
<strong>Submitted By:</strong> {run_data.get('submitted_by', 'Unknown')}
|
| 826 |
+
</div>
|
| 827 |
+
</div>
|
| 828 |
+
</div>
|
| 829 |
+
"""
|
| 830 |
+
|
| 831 |
+
# Generate run report card HTML
|
| 832 |
+
run_card_html_content = generate_run_report_card(run_data)
|
| 833 |
+
|
| 834 |
# Format results for display
|
| 835 |
display_df = results_df.copy()
|
| 836 |
|
|
|
|
| 865 |
run_detail_screen: gr.update(visible=True),
|
| 866 |
run_metadata_html: gr.update(value=metadata_html),
|
| 867 |
test_cases_table: gr.update(value=display_df),
|
| 868 |
+
run_card_html: gr.update(value=run_card_html_content),
|
| 869 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 870 |
}
|
| 871 |
|
|
|
|
| 879 |
run_detail_screen: gr.update(visible=False),
|
| 880 |
run_metadata_html: gr.update(),
|
| 881 |
test_cases_table: gr.update(),
|
| 882 |
+
run_card_html: gr.update(),
|
| 883 |
selected_row_index: gr.update(value="") # Clear textbox
|
| 884 |
}
|
| 885 |
|
|
|
|
| 904 |
# Generate performance chart
|
| 905 |
perf_chart = create_performance_charts(results_df)
|
| 906 |
|
| 907 |
+
# Create metadata HTML
|
| 908 |
+
metadata_html = f"""
|
| 909 |
+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 910 |
+
padding: 20px; border-radius: 10px; color: white; margin-bottom: 20px;">
|
| 911 |
+
<h2 style="margin: 0 0 10px 0;">π Run Detail: {run_data.get('model', 'Unknown')}</h2>
|
| 912 |
+
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; margin-top: 15px;">
|
| 913 |
+
<div>
|
| 914 |
+
<strong>Agent Type:</strong> {run_data.get('agent_type', 'N/A')}<br>
|
| 915 |
+
<strong>Provider:</strong> {run_data.get('provider', 'N/A')}<br>
|
| 916 |
+
<strong>Success Rate:</strong> {run_data.get('success_rate', 0):.1f}%
|
| 917 |
+
</div>
|
| 918 |
+
<div>
|
| 919 |
+
<strong>Total Tests:</strong> {run_data.get('total_tests', 0)}<br>
|
| 920 |
+
<strong>Successful:</strong> {run_data.get('successful_tests', 0)}<br>
|
| 921 |
+
<strong>Failed:</strong> {run_data.get('failed_tests', 0)}
|
| 922 |
+
</div>
|
| 923 |
+
<div>
|
| 924 |
+
<strong>Total Cost:</strong> ${run_data.get('total_cost_usd', 0):.4f}<br>
|
| 925 |
+
<strong>Avg Duration:</strong> {run_data.get('avg_duration_ms', 0):.0f}ms<br>
|
| 926 |
+
<strong>Submitted By:</strong> {run_data.get('submitted_by', 'Unknown')}
|
| 927 |
+
</div>
|
| 928 |
+
</div>
|
| 929 |
+
</div>
|
| 930 |
+
"""
|
| 931 |
+
|
| 932 |
+
# Generate run report card HTML
|
| 933 |
+
run_card_html_content = generate_run_report_card(run_data)
|
| 934 |
+
|
| 935 |
# Format results for display
|
| 936 |
display_df = results_df.copy()
|
| 937 |
|
|
|
|
| 1002 |
leaderboard_screen: gr.update(visible=True),
|
| 1003 |
run_detail_screen: gr.update(visible=False),
|
| 1004 |
run_metadata_html: gr.update(value="<h3>No results dataset found</h3>"),
|
| 1005 |
+
test_cases_table: gr.update(value=pd.DataFrame()),
|
| 1006 |
+
performance_charts: gr.update(),
|
| 1007 |
+
run_card_html: gr.update()
|
| 1008 |
}
|
| 1009 |
|
| 1010 |
results_df = data_loader.load_results(results_dataset)
|
|
|
|
| 1037 |
</div>
|
| 1038 |
"""
|
| 1039 |
|
| 1040 |
+
# Generate run report card HTML
|
| 1041 |
+
run_card_html_content = generate_run_report_card(run_data)
|
| 1042 |
+
|
| 1043 |
# Format results for display
|
| 1044 |
display_df = results_df.copy()
|
| 1045 |
|
|
|
|
| 1074 |
run_detail_screen: gr.update(visible=True),
|
| 1075 |
run_metadata_html: gr.update(value=metadata_html),
|
| 1076 |
test_cases_table: gr.update(value=display_df),
|
| 1077 |
+
performance_charts: gr.update(value=perf_chart),
|
| 1078 |
+
run_card_html: gr.update(value=run_card_html_content)
|
| 1079 |
}
|
| 1080 |
|
| 1081 |
except Exception as e:
|
|
|
|
| 1089 |
leaderboard_screen: gr.update(visible=True), # Stay on leaderboard
|
| 1090 |
run_detail_screen: gr.update(visible=False),
|
| 1091 |
run_metadata_html: gr.update(value="<h3>Error loading run detail</h3>"),
|
| 1092 |
+
test_cases_table: gr.update(value=pd.DataFrame()),
|
| 1093 |
+
performance_charts: gr.update(),
|
| 1094 |
+
run_card_html: gr.update()
|
| 1095 |
}
|
| 1096 |
|
| 1097 |
|
|
|
|
| 1501 |
# Navigation
|
| 1502 |
with gr.Row():
|
| 1503 |
back_to_leaderboard_btn = gr.Button("β¬
οΈ Back to Leaderboard", variant="secondary", size="sm")
|
| 1504 |
+
download_run_card_btn = gr.Button("π₯ Download Run Report Card", variant="secondary", size="sm")
|
| 1505 |
|
| 1506 |
run_detail_title = gr.Markdown("# π Run Detail")
|
| 1507 |
|
|
|
|
| 1523 |
gr.Markdown("*Performance metrics and charts*")
|
| 1524 |
performance_charts = gr.Plot(label="Performance Analysis", show_label=False)
|
| 1525 |
|
| 1526 |
+
with gr.TabItem("π Report Card"):
|
| 1527 |
+
gr.Markdown("*Downloadable run summary card*")
|
| 1528 |
+
run_card_html = gr.HTML(label="Run Report Card", value="<p style='text-align: center; color: #666; padding: 40px;'>Select a run to view its report card</p>")
|
| 1529 |
+
|
| 1530 |
# Screen 4: Trace Detail with Sub-tabs
|
| 1531 |
with gr.Column(visible=False) as trace_detail_screen:
|
| 1532 |
with gr.Row():
|
|
|
|
| 1749 |
generate_card_btn.click(
|
| 1750 |
fn=generate_card,
|
| 1751 |
inputs=[top_n_slider],
|
| 1752 |
+
outputs=[card_preview, download_card_btn]
|
| 1753 |
+
)
|
| 1754 |
+
|
| 1755 |
+
# Download leaderboard summary card as PNG
|
| 1756 |
+
download_card_btn.click(
|
| 1757 |
+
fn=None,
|
| 1758 |
+
js=download_card_as_png_js("summary-card-html")
|
| 1759 |
)
|
| 1760 |
|
| 1761 |
app.load(
|
|
|
|
| 1823 |
leaderboard_table.select(
|
| 1824 |
fn=on_drilldown_select,
|
| 1825 |
inputs=[leaderboard_table], # Pass dataframe to handler (like MockTraceMind)
|
| 1826 |
+
outputs=[leaderboard_screen, run_detail_screen, run_metadata_html, test_cases_table, performance_charts, run_card_html]
|
| 1827 |
)
|
| 1828 |
|
| 1829 |
back_to_leaderboard_btn.click(
|
|
|
|
| 1861 |
selected_row_index.change(
|
| 1862 |
fn=on_html_table_row_click,
|
| 1863 |
inputs=[selected_row_index],
|
| 1864 |
+
outputs=[leaderboard_screen, run_detail_screen, run_metadata_html, test_cases_table, run_card_html, selected_row_index]
|
| 1865 |
+
)
|
| 1866 |
+
|
| 1867 |
+
# Download run report card as PNG
|
| 1868 |
+
download_run_card_btn.click(
|
| 1869 |
+
fn=None,
|
| 1870 |
+
js=download_card_as_png_js(element_id="run-card-html")
|
| 1871 |
)
|
| 1872 |
|
| 1873 |
|