Spaces:
Runtime error
Runtime error
import gradio as gr | |
import bittensor as bt | |
import requests | |
import pandas as pd | |
from apscheduler.schedulers.background import BackgroundScheduler | |
background_url2 = 'https://media.discordapp.net/attachments/1294437373773484093/1305702292774719569/80-_.png?ex=6733fd85&is=6732ac05&hm=f94d26610cc6c9d39318a102fb2484eae6ab83a64226eba3af81fbe305245fc2&=&format=webp&quality=lossless&width=717&height=414' | |
background_url = 'https://media.discordapp.net/attachments/1294437373773484093/1299205237877510184/p7zQRQMPb8W1qitaFl_C9_c6ef80e2981d4a27a53c3708de43bb44.jpg?ex=67336cea&is=67321b6a&hm=aaa1d39210d631d9f800802da8a869c71e395802574ae61bee6030671f53ef3c&=&format=webp' | |
# Enhanced Custom CSS | |
custom_css = """ | |
#component-0 { | |
max-width: 100% !important; | |
padding: 0 !important; | |
margin: 0 !important; | |
} | |
.gradio-container { | |
max-width: 100% !important; | |
padding: 0 !important; | |
margin: 0 !important; | |
background-image: url('""" + background_url + """') !important; | |
background-size: cover !important; | |
background-position: center !important; | |
background-repeat: no-repeat !important; | |
min-height: 100vh !important; | |
} | |
""" | |
# Add error handling for bittensor initialization | |
try: | |
subtensor = bt.subtensor() | |
metagraph = bt.metagraph(netuid=36) | |
except Exception as e: | |
print(f"Failed to initialize bittensor: {e}") | |
subtensor = None | |
metagraph = None | |
def get_validator_data() -> pd.DataFrame: | |
if subtensor is None or metagraph is None: | |
# Return empty DataFrame with correct columns if initialization failed | |
return pd.DataFrame(columns=['Name', 'UID', 'Axon', 'API', 'Step', 'Recent Bits', 'Updated', 'VTrust']) | |
try: | |
validator_ids = list(set([i for i in range(len(metagraph.validator_permit)) | |
if metagraph.validator_permit[i] and | |
metagraph.active[i] and | |
str(metagraph.axons[i].ip) != "0.0.0.0"])) | |
except Exception as e: | |
print(f"Error getting validator IDs: {e}") | |
validator_ids = [] | |
current_block = subtensor.block | |
results = [] | |
for uid in validator_ids: | |
validator_info = { | |
'Name': 'unavailable', | |
'UID': uid, | |
'Axon': 'unavailable', | |
'Step': 0, | |
'Recent Bits': 0, | |
'Updated': 0, | |
'VTrust': 0, | |
'API': 'β' | |
} | |
try: | |
# Get validator name | |
try: | |
identity = subtensor.substrate.query('SubtensorModule', 'Identities', [metagraph.coldkeys[uid]]) | |
validator_info['Name'] = identity.value["name"] if identity != None else 'unnamed' | |
except Exception as e: | |
print(f"Error getting Name for UID {uid}: {str(e)}") | |
validator_info['Axon'] = f"{metagraph.axons[uid].ip}:{metagraph.axons[uid].port}" | |
# Get Step and Range from endpoints | |
try: | |
axon_endpoint = f"http://{validator_info['Axon']}" | |
step_response = requests.get(f"{axon_endpoint}/step", timeout=5) | |
step_response.raise_for_status() | |
validator_info['Step'] = step_response.json() | |
bits_response = requests.get( | |
f"{axon_endpoint}/bits", | |
headers={"range": "bytes=-1"}, | |
timeout=5 | |
) | |
bits_response.raise_for_status() | |
binary_string = ''.join(format(byte, '08b') for byte in bits_response.content) | |
validator_info['Recent Bits'] = binary_string[-8:] | |
validator_info['API'] = '<span class="api-status api-up">β </span>' if bits_response.ok else '<span class="api-status api-down">β</span>' | |
except requests.Timeout: | |
print(f"Timeout while connecting to {axon_endpoint}") | |
except Exception as e: | |
print(f"Error connecting to {axon_endpoint}: {e}") | |
try: | |
last_update = int(metagraph.last_update[uid]) | |
validator_info['Updated'] = current_block - last_update | |
except Exception as e: | |
print(f"Error getting Updated for UID {uid}: {str(e)}") | |
try: | |
validator_info['VTrust'] = float(metagraph.validator_trust[uid]) | |
except Exception as e: | |
print(f"Error getting VTrust for UID {uid}: {str(e)}") | |
except Exception as e: | |
print(f"Error getting Axon for UID {uid}: {str(e)}") | |
results.append(validator_info) | |
df = pd.DataFrame(results) | |
df['VTrust'] = df['VTrust'].round(4) | |
return df.sort_values('Step', ascending=False)[['Name', 'UID', 'Axon', 'API', 'Step', 'Recent Bits', 'Updated', 'VTrust']] | |
# Create the Gradio interface with custom theme | |
app = gr.Blocks( | |
title="SN36 Validator Leaderboard", | |
css=custom_css, | |
theme=gr.themes.Soft().set( | |
body_background_fill="rgba(17, 24, 39, 0.95)", | |
background_fill_secondary="rgba(17, 24, 39, 0.95)", | |
) | |
) | |
# Update the HTML template | |
header_html = """ | |
<div style="text-align: center; max-width: 100%; padding: 1rem; background-color: #1f2937; border-radius: 1rem;"> | |
<h1 style="color: white;">SN36 Validator Leaderboard</h1> | |
<p style="color: white;">Real-time tracking of validator performance and bits</p> | |
</div> | |
""" | |
with app: | |
gr.HTML(header_html) | |
with gr.Tabs(elem_id="main-tabs"): | |
with gr.Tab("π Leaderboard", elem_id="leaderboard-tab"): | |
leaderboard = gr.DataFrame( | |
headers=["Name", "UID", "Axon", "API", "Step", "Recent Bits", "Updated", "VTrust"], | |
datatype=["str", "number", "str", "html", "number", "str", "number", "number"], | |
elem_id="leaderboard-table", | |
render=True | |
) | |
with gr.Row(equal_height=True): | |
refresh_button = gr.Button("π Refresh Data", variant="primary", elem_classes=["refresh-btn"]) | |
auto_refresh = gr.Checkbox( | |
label="Auto-refresh (5 min)", | |
value=True, | |
interactive=True | |
) | |
status_message = gr.Markdown("Last updated: Never", elem_classes=["status-msg"]) | |
with gr.Tab("βΉοΈ About"): | |
gr.Markdown( | |
""" | |
<div style="color: white;"> | |
## About this Leaderboard | |
This dashboard shows real-time information about validators on the network: | |
- **Name**: Validator's registered name on the network | |
- **UID**: Unique identifier of the validator | |
- **Axon**: Validator's Axon address (IP:port) | |
- **API**: API status (β online, β offline) | |
- **Step**: Current step count (0 if unavailable) | |
- **Range**: Validator's bit range (0 if unavailable) | |
- **Updated**: Blocks since last update (0 if unavailable) | |
- **VTrust**: Validator's trust score (0 if unavailable) | |
Data is automatically refreshed every 5 minutes, or you can manually refresh using the button. | |
</div> | |
""" | |
) | |
def update_leaderboard(): | |
df = get_validator_data() | |
timestamp = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S UTC") | |
return df, f"Last updated: {timestamp}" | |
refresh_button.click( | |
fn=update_leaderboard, | |
outputs=[leaderboard, status_message], | |
queue=False | |
) | |
# Auto-refresh logic | |
def setup_auto_refresh(): | |
app.scheduler = BackgroundScheduler() | |
app.scheduler.add_job( | |
lambda: app.queue(update_leaderboard), | |
'interval', | |
minutes=5 | |
) | |
app.scheduler.start() | |
# Initial data load | |
app.load( | |
fn=update_leaderboard, | |
outputs=[leaderboard, status_message] | |
) | |
setup_auto_refresh() | |
# Launch the interface with file serving enabled | |
app.launch( | |
share=False | |
) |