gauravlochab commited on
Commit
45a247a
·
1 Parent(s): 5aa3a66

feat: adding baseline for performance graph

Browse files
Files changed (2) hide show
  1. app.py +88 -3
  2. apr_vs_agent_hash.py +488 -0
app.py CHANGED
@@ -17,6 +17,8 @@ import logging
17
  from typing import List, Dict, Any, Optional
18
  # Comment out the import for now and replace with dummy functions
19
  # from app_trans_new import create_transcation_visualizations,create_active_agents_visualizations
 
 
20
  # APR visualization functions integrated directly
21
 
22
  # Set up logging with appropriate verbosity
@@ -191,7 +193,14 @@ def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
191
  if portfolio and isinstance(portfolio, dict):
192
  volume = portfolio.get("volume")
193
 
194
- logger.debug(f"Agent {agent_id}: Raw APR value: {apr}, adjusted APR value: {adjusted_apr}, ROI value: {roi}, volume: {volume}, timestamp: {timestamp}")
 
 
 
 
 
 
 
195
 
196
  # Convert timestamp to datetime if it exists
197
  timestamp_dt = None
@@ -204,7 +213,8 @@ def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
204
  "roi": roi,
205
  "volume": volume,
206
  "timestamp": timestamp_dt,
207
- "agent_id": agent_id,
 
208
  "is_dummy": False
209
  }
210
  logger.debug(f"Agent {agent_id}: Extracted result: {result}")
@@ -2949,7 +2959,7 @@ def dashboard():
2949
  with gr.Blocks() as demo:
2950
  gr.Markdown("# Average Modius Agent Performance")
2951
 
2952
- # Create tabs for APR, ROI, and Volume metrics
2953
  with gr.Tabs():
2954
  # APR Metrics tab
2955
  with gr.Tab("APR Metrics"):
@@ -3019,6 +3029,18 @@ def dashboard():
3019
 
3020
  # Add a text area for status messages
3021
  volume_status_text = gr.Textbox(label="Status", value="Ready", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
3022
 
3023
  # Add custom CSS for making the plots responsive
3024
  gr.HTML("""
@@ -3207,6 +3229,16 @@ def dashboard():
3207
  )
3208
  combined_volume_graph.value = volume_placeholder_fig
3209
 
 
 
 
 
 
 
 
 
 
 
3210
  # Function to update the APR graph based on toggle states
3211
  def update_apr_graph_with_toggles(apr_visible, adjusted_apr_visible):
3212
  return update_apr_graph(apr_visible, adjusted_apr_visible)
@@ -3334,6 +3366,59 @@ def dashboard():
3334
  inputs=[volume_toggle],
3335
  outputs=[combined_volume_graph]
3336
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3337
 
3338
  return demo
3339
 
 
17
  from typing import List, Dict, Any, Optional
18
  # Comment out the import for now and replace with dummy functions
19
  # from app_trans_new import create_transcation_visualizations,create_active_agents_visualizations
20
+ # Import APR vs agent hash visualization functions
21
+ from apr_vs_agent_hash import generate_apr_vs_agent_hash_visualizations
22
  # APR visualization functions integrated directly
23
 
24
  # Set up logging with appropriate verbosity
 
193
  if portfolio and isinstance(portfolio, dict):
194
  volume = portfolio.get("volume")
195
 
196
+ # Extract agent_hash from json_data or portfolio_snapshot
197
+ agent_hash = json_data.get("agent_hash")
198
+ if agent_hash is None and "portfolio_snapshot" in json_data and json_data["portfolio_snapshot"] is not None:
199
+ portfolio = json_data["portfolio_snapshot"].get("portfolio")
200
+ if portfolio and isinstance(portfolio, dict):
201
+ agent_hash = portfolio.get("agent_hash")
202
+
203
+ logger.debug(f"Agent {agent_id}: Raw APR value: {apr}, adjusted APR value: {adjusted_apr}, ROI value: {roi}, volume: {volume}, timestamp: {timestamp}, agent_hash: {agent_hash}")
204
 
205
  # Convert timestamp to datetime if it exists
206
  timestamp_dt = None
 
213
  "roi": roi,
214
  "volume": volume,
215
  "timestamp": timestamp_dt,
216
+ "agent_id": agent_id,
217
+ "agent_hash": agent_hash,
218
  "is_dummy": False
219
  }
220
  logger.debug(f"Agent {agent_id}: Extracted result: {result}")
 
2959
  with gr.Blocks() as demo:
2960
  gr.Markdown("# Average Modius Agent Performance")
2961
 
2962
+ # Create tabs for APR, ROI, Volume, and APR vs Agent Hash metrics
2963
  with gr.Tabs():
2964
  # APR Metrics tab
2965
  with gr.Tab("APR Metrics"):
 
3029
 
3030
  # Add a text area for status messages
3031
  volume_status_text = gr.Textbox(label="Status", value="Ready", interactive=False)
3032
+
3033
+ # APR vs Agent Hash tab
3034
+ with gr.Tab("APR vs Agent Hash"):
3035
+ with gr.Column():
3036
+ refresh_apr_hash_btn = gr.Button("Refresh APR vs Agent Hash Data")
3037
+
3038
+ # Create container for plotly figure with responsive sizing
3039
+ with gr.Column():
3040
+ apr_vs_agent_hash_graph = gr.Plot(label="APR vs Agent Hash", elem_id="responsive_apr_hash_plot")
3041
+
3042
+ # Add a text area for status messages
3043
+ apr_hash_status_text = gr.Textbox(label="Status", value="Ready", interactive=False)
3044
 
3045
  # Add custom CSS for making the plots responsive
3046
  gr.HTML("""
 
3229
  )
3230
  combined_volume_graph.value = volume_placeholder_fig
3231
 
3232
+ # Initialize the APR vs Agent Hash graph on load with a placeholder
3233
+ apr_hash_placeholder_fig = go.Figure()
3234
+ apr_hash_placeholder_fig.add_annotation(
3235
+ text="Click 'Refresh APR vs Agent Hash Data' to load APR vs Agent Hash graph",
3236
+ x=0.5, y=0.5,
3237
+ showarrow=False,
3238
+ font=dict(size=15)
3239
+ )
3240
+ apr_vs_agent_hash_graph.value = apr_hash_placeholder_fig
3241
+
3242
  # Function to update the APR graph based on toggle states
3243
  def update_apr_graph_with_toggles(apr_visible, adjusted_apr_visible):
3244
  return update_apr_graph(apr_visible, adjusted_apr_visible)
 
3366
  inputs=[volume_toggle],
3367
  outputs=[combined_volume_graph]
3368
  )
3369
+
3370
+ # Function to update the APR vs Agent Hash graph
3371
+ def update_apr_vs_agent_hash_graph():
3372
+ """Update the APR vs Agent Hash graph"""
3373
+ try:
3374
+ # Generate visualization and get figure object directly
3375
+ fig, _ = generate_apr_vs_agent_hash_visualizations(global_df)
3376
+ return fig
3377
+ except Exception as e:
3378
+ logger.exception("Error generating APR vs Agent Hash visualization")
3379
+ # Create error figure
3380
+ error_fig = go.Figure()
3381
+ error_fig.add_annotation(
3382
+ text=f"Error: {str(e)}",
3383
+ x=0.5, y=0.5,
3384
+ showarrow=False,
3385
+ font=dict(size=15, color="red")
3386
+ )
3387
+ return error_fig
3388
+
3389
+ # Function to refresh APR vs Agent Hash data
3390
+ def refresh_apr_vs_agent_hash_data():
3391
+ """Refresh APR vs Agent Hash data from the database and update the visualization"""
3392
+ try:
3393
+ # Fetch new APR data if not already fetched
3394
+ logger.info("Manually refreshing APR vs Agent Hash data...")
3395
+ if global_df is None or global_df.empty:
3396
+ fetch_apr_data_from_db()
3397
+
3398
+ # Verify data was fetched successfully
3399
+ if global_df is None or len(global_df) == 0:
3400
+ logger.error("Failed to fetch APR data for APR vs Agent Hash visualization")
3401
+ return apr_vs_agent_hash_graph.value, "Error: Failed to fetch APR data. Check the logs for details."
3402
+
3403
+ # Check if agent_hash column exists
3404
+ if 'agent_hash' not in global_df.columns:
3405
+ logger.error("agent_hash column not found in DataFrame")
3406
+ return apr_vs_agent_hash_graph.value, "Error: agent_hash column not found in data. Check the logs for details."
3407
+
3408
+ # Generate new visualization
3409
+ logger.info("Generating new APR vs Agent Hash visualization...")
3410
+ new_graph = update_apr_vs_agent_hash_graph()
3411
+ return new_graph, "APR vs Agent Hash data refreshed successfully"
3412
+ except Exception as e:
3413
+ logger.error(f"Error refreshing APR vs Agent Hash data: {e}")
3414
+ return apr_vs_agent_hash_graph.value, f"Error: {str(e)}"
3415
+
3416
+ # Set up the button click event for APR vs Agent Hash refresh
3417
+ refresh_apr_hash_btn.click(
3418
+ fn=refresh_apr_vs_agent_hash_data,
3419
+ inputs=[],
3420
+ outputs=[apr_vs_agent_hash_graph, apr_hash_status_text]
3421
+ )
3422
 
3423
  return demo
3424
 
apr_vs_agent_hash.py ADDED
@@ -0,0 +1,488 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import plotly.graph_objects as go
3
+ import plotly.express as px
4
+ from datetime import datetime
5
+ import logging
6
+ import json
7
+ import os
8
+
9
+ # Set up logging
10
+ logger = logging.getLogger(__name__)
11
+
12
+ def create_apr_vs_agent_hash_graph(df):
13
+ """
14
+ Create a box plot showing APR values distribution for each agent hash version.
15
+
16
+ Args:
17
+ df: DataFrame containing the APR data with agent_hash column
18
+
19
+ Returns:
20
+ A Plotly figure object
21
+ """
22
+ if len(df) == 0 or 'agent_hash' not in df.columns:
23
+ logger.error("No data or agent_hash column not found to plot APR vs agent hash graph")
24
+ fig = go.Figure()
25
+ fig.add_annotation(
26
+ text="No agent hash data available",
27
+ x=0.5, y=0.5,
28
+ showarrow=False, font=dict(size=20)
29
+ )
30
+ return fig
31
+
32
+ # Filter for APR data only and ensure agent_hash is not null
33
+ apr_data = df[(df['metric_type'] == 'APR') & (df['agent_hash'].notna())].copy()
34
+
35
+ if len(apr_data) == 0:
36
+ logger.error("No valid APR data with agent_hash found")
37
+ fig = go.Figure()
38
+ fig.add_annotation(
39
+ text="No valid APR data with agent_hash found",
40
+ x=0.5, y=0.5,
41
+ showarrow=False, font=dict(size=20)
42
+ )
43
+ return fig
44
+
45
+ # Filter out outliers (APR values above 200 or below -200)
46
+ outlier_data = apr_data[(apr_data['apr'] > 200) | (apr_data['apr'] < -200)].copy()
47
+ apr_data_filtered = apr_data[(apr_data['apr'] <= 200) & (apr_data['apr'] >= -200)].copy()
48
+
49
+ # Log the outliers for better debugging
50
+ if len(outlier_data) > 0:
51
+ excluded_count = len(outlier_data)
52
+ logger.info(f"Excluded {excluded_count} data points with outlier APR values (>200 or <-200)")
53
+
54
+ # Group outliers by agent for detailed logging
55
+ outlier_agents = outlier_data.groupby('agent_name')
56
+ for agent_name, agent_outliers in outlier_agents:
57
+ logger.info(f"Agent '{agent_name}' has {len(agent_outliers)} outlier values:")
58
+ for idx, row in agent_outliers.iterrows():
59
+ logger.info(f" - APR: {row['apr']}, timestamp: {row['timestamp']}, agent_hash: {row['agent_hash']}")
60
+
61
+ # Use the filtered data for all subsequent operations
62
+ apr_data = apr_data_filtered
63
+
64
+ # Create Plotly figure
65
+ fig = go.Figure()
66
+
67
+ # Add a zero line that spans the entire width
68
+ fig.add_shape(
69
+ type="line",
70
+ line=dict(dash="solid", width=1.5, color="black"),
71
+ y0=0, y1=0,
72
+ x0=-0.5, x1=10, # Will be adjusted later based on number of boxes
73
+ layer="below"
74
+ )
75
+
76
+ # Add background shapes for positive and negative regions
77
+ # These will be adjusted later based on the actual x-axis range
78
+ fig.add_shape(
79
+ type="rect",
80
+ fillcolor="rgba(230, 243, 255, 0.3)",
81
+ line=dict(width=0),
82
+ y0=0, y1=100, # Use a fixed positive value
83
+ x0=-0.5, x1=10, # Will be adjusted later
84
+ layer="below"
85
+ )
86
+
87
+ fig.add_shape(
88
+ type="rect",
89
+ fillcolor="rgba(255, 230, 230, 0.3)",
90
+ line=dict(width=0),
91
+ y0=-100, y1=0, # Use a fixed negative value
92
+ x0=-0.5, x1=10, # Will be adjusted later
93
+ layer="below"
94
+ )
95
+
96
+ # Group by agent_hash
97
+ unique_hashes = apr_data['agent_hash'].unique()
98
+
99
+ # Map for version labels based on hash endings
100
+ version_map = {}
101
+ for hash_val in unique_hashes:
102
+ if hash_val.endswith("tby"):
103
+ version_map[hash_val] = "v0.4.1"
104
+ elif hash_val.endswith("vq"):
105
+ version_map[hash_val] = "v0.4.2"
106
+ else:
107
+ # For any other hashes, use the last 6 characters
108
+ version_map[hash_val] = f"Hash: {hash_val[-6:]}"
109
+
110
+ # Sort hashes by version (v0.4.1 first, then v0.4.2)
111
+ sorted_hashes = sorted(unique_hashes, key=lambda h: "1" if h.endswith("tby") else "2" if h.endswith("vq") else h)
112
+
113
+ # Colors for different versions
114
+ version_colors = {
115
+ "v0.4.1": "rgba(31, 119, 180, 0.7)", # Blue
116
+ "v0.4.2": "rgba(44, 160, 44, 0.7)", # Green
117
+ }
118
+
119
+ # Default color for other hashes
120
+ default_color = "rgba(214, 39, 40, 0.7)" # Red
121
+
122
+ # Prepare data for box plots and statistics
123
+ box_data = []
124
+ version_stats = {}
125
+
126
+ # X-axis positions and labels
127
+ x_positions = []
128
+ x_labels = []
129
+
130
+ # Process each hash to create box plot data
131
+ for i, agent_hash in enumerate(sorted_hashes):
132
+ hash_data = apr_data[apr_data['agent_hash'] == agent_hash]
133
+
134
+ # Get agent name for this hash (should be the same for all records with this hash)
135
+ agent_name = hash_data['agent_name'].iloc[0] if not hash_data.empty else "Unknown"
136
+
137
+ # Get version label
138
+ version = version_map[agent_hash]
139
+
140
+ # Choose color based on version
141
+ if version in version_colors:
142
+ color = version_colors[version]
143
+ else:
144
+ color = default_color
145
+
146
+ # Calculate statistics for this hash
147
+ apr_values = hash_data['apr'].tolist()
148
+ median_apr = hash_data['apr'].median()
149
+ mean_apr = hash_data['apr'].mean()
150
+ min_apr = hash_data['apr'].min()
151
+ max_apr = hash_data['apr'].max()
152
+ count = len(apr_values)
153
+
154
+ # Store statistics for later use
155
+ if version not in version_stats:
156
+ version_stats[version] = {
157
+ 'apr_values': [],
158
+ 'count': 0,
159
+ 'hashes': []
160
+ }
161
+
162
+ version_stats[version]['apr_values'].extend(apr_values)
163
+ version_stats[version]['count'] += count
164
+ version_stats[version]['hashes'].append(agent_hash)
165
+
166
+ # Create label with version only (no hash)
167
+ label = f"{version}"
168
+
169
+ # Add to x-axis positions and labels
170
+ x_positions.append(i)
171
+ x_labels.append(label)
172
+
173
+ # Create hover text with detailed statistics
174
+ hover_text = (
175
+ f"Version: {version}<br>"
176
+ f"Agent: {agent_name}<br>"
177
+ f"Hash: {agent_hash}<br>"
178
+ f"Median APR: {median_apr:.2f}%<br>"
179
+ f"Mean APR: {mean_apr:.2f}%<br>"
180
+ f"Min APR: {min_apr:.2f}%<br>"
181
+ f"Max APR: {max_apr:.2f}%<br>"
182
+ f"Data points: {count}"
183
+ )
184
+
185
+ # Add box plot for this hash
186
+ fig.add_trace(
187
+ go.Box(
188
+ y=apr_values,
189
+ x=[i] * len(apr_values), # Position on x-axis
190
+ name=label,
191
+ boxpoints='outliers', # Show only outlier points instead of all points
192
+ jitter=0.1, # Reduced jitter for less horizontal spread
193
+ pointpos=0, # Position of points relative to box
194
+ marker=dict(
195
+ color=color,
196
+ size=6, # Smaller point size
197
+ opacity=0.7, # Add transparency
198
+ line=dict(width=1, color='black')
199
+ ),
200
+ line=dict(
201
+ color='black',
202
+ width=2 # Thicker line for better visibility
203
+ ),
204
+ fillcolor=color,
205
+ hoverinfo='text',
206
+ hovertext=hover_text,
207
+ showlegend=False,
208
+ boxmean=True, # Show mean as a dashed line
209
+ whiskerwidth=0.8, # Slightly thinner whiskers
210
+ width=0.6 # Wider boxes
211
+ )
212
+ )
213
+
214
+ logger.info(f"Added box plot for agent hash {agent_hash} ({version}) with {count} points")
215
+
216
+ # Add text annotation with median value above each box
217
+ fig.add_annotation(
218
+ x=i,
219
+ y=median_apr + 5, # Position above the box
220
+ text=f"{median_apr:.1f}%",
221
+ showarrow=False,
222
+ font=dict(
223
+ family="Arial, sans-serif",
224
+ size=12,
225
+ color="black",
226
+ weight="bold"
227
+ )
228
+ )
229
+
230
+ # Calculate improvement metrics between versions
231
+ if "v0.4.1" in version_stats and "v0.4.2" in version_stats:
232
+ v041_values = version_stats["v0.4.1"]["apr_values"]
233
+ v042_values = version_stats["v0.4.2"]["apr_values"]
234
+
235
+ v041_median = pd.Series(v041_values).median()
236
+ v042_median = pd.Series(v042_values).median()
237
+
238
+ improvement = v042_median - v041_median
239
+ improvement_pct = (improvement / abs(v041_median)) * 100 if v041_median != 0 else float('inf')
240
+
241
+ # Determine if the change is positive or negative
242
+ is_improvement = improvement > 0
243
+ change_color = "green" if is_improvement else "red"
244
+ change_text = "improvement" if is_improvement else "decrease"
245
+
246
+ # Add annotation showing improvement with better styling
247
+ fig.add_annotation(
248
+ x=(len(sorted_hashes) - 1) / 2, # Center of the x-axis
249
+ y=90, # Top of the chart
250
+ text=f"<b>Version Comparison:</b> {abs(improvement):.2f}% {change_text} from v0.4.1 to v0.4.2",
251
+ showarrow=False,
252
+ font=dict(
253
+ family="Arial, sans-serif",
254
+ size=16,
255
+ color=change_color,
256
+ weight="bold"
257
+ ),
258
+ bgcolor="rgba(255, 255, 255, 0.9)",
259
+ bordercolor=change_color,
260
+ borderwidth=2,
261
+ borderpad=6,
262
+ opacity=0.9
263
+ )
264
+
265
+ # Update the shapes to match the actual x-axis range
266
+ num_boxes = len(sorted_hashes)
267
+ fig.update_shapes(
268
+ dict(x0=-0.5, x1=num_boxes - 0.5),
269
+ selector=dict(type='rect')
270
+ )
271
+ fig.update_shapes(
272
+ dict(x0=-0.5, x1=num_boxes - 0.5),
273
+ selector=dict(type='line')
274
+ )
275
+
276
+ # Update layout with improved styling
277
+ fig.update_layout(
278
+ title=dict(
279
+ text="APR Values by Agent Version",
280
+ font=dict(
281
+ family="Arial, sans-serif",
282
+ size=24, # Larger title
283
+ color="black",
284
+ weight="bold"
285
+ ),
286
+ x=0.5, # Center the title
287
+ y=0.95 # Position slightly higher
288
+ ),
289
+ xaxis_title=dict(
290
+ text="Agent Version",
291
+ font=dict(
292
+ family="Arial, sans-serif",
293
+ size=18, # Larger axis title
294
+ color="black",
295
+ weight="bold"
296
+ )
297
+ ),
298
+ yaxis_title=None, # Remove the y-axis title as we'll use annotations instead
299
+ template="plotly_white",
300
+ height=700, # Increased height for better visualization
301
+ width=900, # Set a fixed width for better proportions
302
+ autosize=True, # Still enable auto-sizing for responsiveness
303
+ boxmode='group', # Group boxes together
304
+ margin=dict(r=50, l=120, t=100, b=100), # Reduced right margin since guide was removed
305
+ hovermode="closest",
306
+ plot_bgcolor='rgba(250,250,250,0.9)', # Slightly off-white background
307
+ paper_bgcolor='white',
308
+ font=dict(
309
+ family="Arial, sans-serif",
310
+ size=14,
311
+ color="black"
312
+ ),
313
+ showlegend=False
314
+ )
315
+
316
+ # Add annotations for y-axis regions
317
+ fig.add_annotation(
318
+ x=-0.08, # Position further from the y-axis to avoid overlapping with tick labels
319
+ y=-25, # Middle of the negative region
320
+ xref="paper",
321
+ yref="y",
322
+ text="Percent drawdown [%]",
323
+ showarrow=False,
324
+ font=dict(size=16, family="Arial, sans-serif", color="black", weight="bold"), # Adjusted font size
325
+ textangle=-90, # Rotate text to be vertical
326
+ align="center"
327
+ )
328
+
329
+ fig.add_annotation(
330
+ x=-0.08, # Position further from the y-axis to avoid overlapping with tick labels
331
+ y=50, # Middle of the positive region
332
+ xref="paper",
333
+ yref="y",
334
+ text="Agent APR [%]",
335
+ showarrow=False,
336
+ font=dict(size=16, family="Arial, sans-serif", color="black", weight="bold"), # Adjusted font size
337
+ textangle=-90, # Rotate text to be vertical
338
+ align="center"
339
+ )
340
+
341
+ # Box plot guide removed as per user request
342
+
343
+ # Update y-axis with fixed range of -50 to +100 for psychological effect
344
+ fig.update_yaxes(
345
+ showgrid=True,
346
+ gridwidth=1,
347
+ gridcolor='rgba(0,0,0,0.1)',
348
+ # Use fixed range instead of autoscaling
349
+ autorange=False, # Disable autoscaling
350
+ range=[-50, 100], # Set fixed range from -50 to +100
351
+ tickformat=".2f", # Format tick labels with 2 decimal places
352
+ tickfont=dict(size=14, family="Arial, sans-serif", color="black", weight="bold"), # Adjusted font size
353
+ title=None # Remove the built-in axis title since we're using annotations
354
+ )
355
+
356
+ # Update x-axis with custom labels
357
+ fig.update_xaxes(
358
+ showgrid=True,
359
+ gridwidth=1,
360
+ gridcolor='rgba(0,0,0,0.1)',
361
+ tickmode='array',
362
+ tickvals=x_positions,
363
+ ticktext=x_labels,
364
+ tickangle=-45, # Angle the labels for better readability
365
+ tickfont=dict(size=14, family="Arial, sans-serif", color="black", weight="bold") # Adjusted font size
366
+ )
367
+
368
+ try:
369
+ # Save the figure
370
+ graph_file = "modius_apr_vs_agent_hash_graph.html"
371
+ fig.write_html(graph_file, include_plotlyjs='cdn', full_html=False)
372
+
373
+ # Also save as image for compatibility
374
+ img_file = "modius_apr_vs_agent_hash_graph.png"
375
+ try:
376
+ fig.write_image(img_file)
377
+ logger.info(f"APR vs agent hash graph saved to {graph_file} and {img_file}")
378
+ except Exception as e:
379
+ logger.error(f"Error saving image: {e}")
380
+ logger.info(f"APR vs agent hash graph saved to {graph_file} only")
381
+
382
+ # Return the figure object for direct use in Gradio
383
+ return fig
384
+ except Exception as e:
385
+ logger.error(f"Error creating APR vs agent hash graph: {e}")
386
+
387
+ # Create a simpler graph as fallback
388
+ simple_fig = go.Figure()
389
+
390
+ # Add zero line
391
+ simple_fig.add_shape(
392
+ type="line",
393
+ line=dict(dash="solid", width=1.5, color="black"),
394
+ y0=0, y1=0,
395
+ x0=-0.5, x1=1.5 # Fixed values for error case
396
+ )
397
+
398
+ # Add a note about the error
399
+ simple_fig.add_annotation(
400
+ text=f"Error creating graph: {str(e)}",
401
+ x=0.5, y=0.5,
402
+ showarrow=False,
403
+ font=dict(size=15, color="red")
404
+ )
405
+
406
+ return simple_fig
407
+
408
+ def save_apr_vs_agent_hash_to_csv(df):
409
+ """
410
+ Save the APR vs agent hash data to a CSV file.
411
+
412
+ Args:
413
+ df: DataFrame containing the APR data with agent_hash column
414
+
415
+ Returns:
416
+ The path to the saved CSV file, or None if no data was saved
417
+ """
418
+ if df.empty or 'agent_hash' not in df.columns:
419
+ logger.error("No data or agent_hash column not found to save to CSV")
420
+ return None
421
+
422
+ # Filter for APR data only and ensure agent_hash is not null
423
+ apr_data = df[(df['metric_type'] == 'APR') & (df['agent_hash'].notna())].copy()
424
+
425
+ if apr_data.empty:
426
+ logger.error("No valid APR data with agent_hash found to save to CSV")
427
+ return None
428
+
429
+ # Define the CSV file path
430
+ csv_file = "modius_apr_vs_agent_hash.csv"
431
+
432
+ # Save to CSV
433
+ apr_data.to_csv(csv_file, index=False)
434
+ logger.info(f"APR vs agent hash data saved to {csv_file}")
435
+
436
+ return csv_file
437
+
438
+ def generate_apr_vs_agent_hash_visualizations(df):
439
+ """
440
+ Generate APR vs agent hash visualizations.
441
+
442
+ Args:
443
+ df: DataFrame containing the APR data
444
+
445
+ Returns:
446
+ A tuple containing the Plotly figure object and the path to the saved CSV file
447
+ """
448
+ if df.empty:
449
+ logger.info("No APR data available for agent hash visualization.")
450
+ # Create empty visualization with a message using Plotly
451
+ fig = go.Figure()
452
+ fig.add_annotation(
453
+ x=0.5, y=0.5,
454
+ text="No APR data available for agent hash visualization",
455
+ font=dict(size=20),
456
+ showarrow=False
457
+ )
458
+ fig.update_layout(
459
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
460
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
461
+ )
462
+
463
+ return fig, None
464
+
465
+ # Check if agent_hash column exists
466
+ if 'agent_hash' not in df.columns:
467
+ logger.error("agent_hash column not found in DataFrame")
468
+ fig = go.Figure()
469
+ fig.add_annotation(
470
+ x=0.5, y=0.5,
471
+ text="agent_hash column not found in data",
472
+ font=dict(size=20),
473
+ showarrow=False
474
+ )
475
+ fig.update_layout(
476
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
477
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
478
+ )
479
+
480
+ return fig, None
481
+
482
+ # Save to CSV before creating visualization
483
+ csv_file = save_apr_vs_agent_hash_to_csv(df)
484
+
485
+ # Create the visualization
486
+ fig = create_apr_vs_agent_hash_graph(df)
487
+
488
+ return fig, csv_file