alex n
fixed api state error
e2ec4cf
raw
history blame
8.72 kB
import gradio as gr
import bittensor as bt
import requests
import pandas as pd
from apscheduler.schedulers.background import BackgroundScheduler
# Enhanced Custom CSS
custom_css = """
.gradio-container {
max-width: 1200px !important;
margin: auto;
background-color: #1a1a1a;
}
.title {
text-align: center;
margin-bottom: 2rem;
padding: 2rem 0;
background: linear-gradient(90deg, #FF4B1F 0%, #FF9068 100%);
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.title h1 {
color: white;
font-size: 2.5rem;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}
.title p {
color: rgba(255, 255, 255, 0.9);
font-size: 1.1rem;
margin: 0.5rem 0 0 0;
}
/* Style the tabs */
.tabs {
margin-top: 1rem;
}
/* Style the DataFrame */
.dataframe {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Style the refresh button */
.refresh-btn {
background: linear-gradient(90deg, #FF4B1F 0%, #FF9068 100%);
border: none;
padding: 10px 20px;
border-radius: 5px;
color: white;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s;
}
.refresh-btn:hover {
transform: translateY(-2px);
}
/* Status message styling */
.status-msg {
color: #888;
font-style: italic;
margin-top: 1rem;
}
/* Custom styling for API status indicators */
.api-status {
font-size: 1.2em;
}
.api-up {
color: #00ff00;
}
.api-down {
color: #ff0000;
}
"""
# 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
app = gr.Blocks(
title="SN36 Validator Leaderboard",
css="""
#component-0 { height: 100vh !important; }
.gradio-container { height: 100vh !important; }
.contain { height: 100vh !important; }
.main { height: 100% !important; }
.tabs { height: calc(100% - 100px) !important; }
.tab-panel { height: 100% !important; }
#leaderboard-table { height: calc(100% - 80px) !important; }
"""
)
# Update the HTML template
header_html = """
<div style="text-align: center; max-width: 100%; padding: 1rem; background-color: #FF5733; 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(
"""
## 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.
"""
)
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
app.launch()