testrun1 / app.py
arvnoodle's picture
Update app.py
0d89d1c verified
import gradio as gr
import plotly.graph_objects as go
import pandas as pd
import numpy as np
def calculate_runpod_cost_revised(
requests_per_hour,
execution_time_per_request,
active_cost_per_second,
flex_cost_per_second,
cold_start_penalty,
num_active_workers,
concurrent_batch_size=1 # How many requests can be processed simultaneously by one worker
):
# Monthly hours and requests
monthly_hours = 24 * 30
seconds_per_hour = 3600
monthly_requests = requests_per_hour * monthly_hours
# Calculate active worker capacity (requests per hour)
# Each worker can process (concurrent_batch_size) requests at a time
active_capacity_per_hour = (seconds_per_hour / execution_time_per_request) * num_active_workers * concurrent_batch_size
# Total requests handled by active workers over the month
active_requests_handled = min(monthly_requests, active_capacity_per_hour * monthly_hours)
# Remaining requests go to flex workers
flex_requests = max(0, monthly_requests - active_requests_handled)
# Active worker cost (constant regardless of actual usage)
active_cost = num_active_workers * active_cost_per_second * seconds_per_hour * monthly_hours
# For flex workers, we calculate cold starts based on request arrival pattern
# This is simplified - in reality depends on actual traffic patterns
avg_requests_per_cold_start = concurrent_batch_size * 2 # Estimate that batches arrive close enough to reuse some workers
cold_starts = flex_requests / avg_requests_per_cold_start if flex_requests > 0 else 0
# Flex worker cost (only pay for processing time + cold starts)
flex_processing_cost = flex_requests * execution_time_per_request * flex_cost_per_second
flex_cold_start_cost = cold_starts * cold_start_penalty * flex_cost_per_second
flex_cost = flex_processing_cost + flex_cold_start_cost
# Active worker utilization
active_utilization = (active_requests_handled / (active_capacity_per_hour * monthly_hours) * 100) if num_active_workers > 0 else 0
total_monthly_cost = active_cost + flex_cost
return {
"monthly_requests": monthly_requests,
"active_requests": active_requests_handled,
"flex_requests": flex_requests,
"active_cost": active_cost,
"flex_cost": flex_cost,
"total_monthly_cost": total_monthly_cost,
"active_utilization": active_utilization
}
def generate_cost_projection(
min_requests,
max_requests,
execution_time,
active_cost,
flex_cost,
num_active_workers,
cold_start,
concurrent_batch_size
):
# Generate data for different request volumes
request_volumes = np.linspace(min_requests, max_requests, 20)
results = []
for req_vol in request_volumes:
result = calculate_runpod_cost_revised(
req_vol,
execution_time,
active_cost,
flex_cost,
cold_start,
num_active_workers,
concurrent_batch_size
)
results.append({
'requests_per_hour': req_vol,
'monthly_requests': result['monthly_requests'],
'active_cost': result['active_cost'],
'flex_cost': result['flex_cost'],
'total_cost': result['total_monthly_cost'],
'active_utilization': result['active_utilization']
})
df = pd.DataFrame(results)
# Create plotly figure
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df['requests_per_hour'],
y=df['active_cost'],
name='Active Worker Cost',
line=dict(color='blue')
))
fig.add_trace(go.Scatter(
x=df['requests_per_hour'],
y=df['flex_cost'],
name='Flex Worker Cost',
line=dict(color='orange')
))
fig.add_trace(go.Scatter(
x=df['requests_per_hour'],
y=df['total_cost'],
name='Total Cost',
line=dict(color='green', width=3)
))
fig.update_layout(
title='RunPod Serverless Monthly Cost Projection',
xaxis_title='Requests per Hour',
yaxis_title='Monthly Cost ($)',
hovermode='x unified'
)
return fig
def calculate_specific_cost(
requests,
execution_time,
active_cost,
flex_cost,
num_active_workers,
cold_start,
concurrent_batch_size
):
result = calculate_runpod_cost_revised(
requests,
execution_time,
active_cost,
flex_cost,
cold_start,
num_active_workers,
concurrent_batch_size
)
# Format the output HTML for the table
html = f"""
<table style="width:100%; border-collapse: collapse;">
<tr style="background-color: #f2f2f2;">
<th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Metric</th>
<th style="padding: 12px; text-align: right; border: 1px solid #ddd;">Value</th>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Monthly Requests</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['monthly_requests']:,.0f}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Requests Handled by Active Workers</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['active_requests']:,.0f}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Requests Handled by Flex Workers</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['flex_requests']:,.0f}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Cost</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['active_cost']:,.2f}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Flex Worker Cost</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['flex_cost']:,.2f}</td>
</tr>
<tr style="font-weight: bold;">
<td style="padding: 8px; border: 1px solid #ddd;">Total Monthly Cost</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['total_monthly_cost']:,.2f}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Utilization</td>
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['active_utilization']:.1f}%</td>
</tr>
</table>
"""
return html
# Create theme
theme = gr.themes.Default(
primary_hue=gr.themes.colors.red,
secondary_hue=gr.themes.colors.red,
neutral_hue=gr.themes.colors.slate,
)
# Create Gradio interface
with gr.Blocks(title="RunPod Serverless Cost Estimator", theme=theme) as demo:
gr.Markdown("# RunPod Serverless Cost Estimator")
gr.Markdown("Estimate your monthly RunPod Serverless costs based on request volume and execution time")
with gr.Row():
with gr.Column(scale=1):
min_requests = gr.Slider(minimum=100, maximum=50000, value=1000, step=1000, label="Minimum Requests/Hour")
max_requests = gr.Slider(minimum=1000, maximum=100000, value=20000, step=1000, label="Maximum Requests/Hour")
execution_time = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Execution Time per Request (seconds)")
active_cost = gr.Number(value=0.00019, label="Active Worker Cost ($/second)", precision=5)
flex_cost = gr.Number(value=0.00031, label="Flex Worker Cost ($/second)", precision=5)
num_active_workers = gr.Slider(minimum=0, maximum=600, value=30, step=10, label="Number of Active Workers")
cold_start = gr.Slider(minimum=0, maximum=60, value=1, step=1, label="Cold Start Penalty (seconds)")
concurrent_batch_size = gr.Slider(minimum=1, maximum=10, value=2, step=1, label="Concurrent Requests per Worker")
plot_button = gr.Button("Generate Cost Projection")
with gr.Column(scale=2):
plot_output = gr.Plot(label="Cost Projection")
gr.Markdown("## Cost Breakdown for Specific Request Volume")
with gr.Row():
with gr.Column(scale=1):
specific_requests = gr.Number(value=10000, label="Requests per Hour", precision=0)
calc_button = gr.Button("Calculate Cost")
with gr.Column(scale=2):
cost_table = gr.HTML()
plot_button.click(
generate_cost_projection,
inputs=[min_requests, max_requests, execution_time, active_cost, flex_cost, num_active_workers, cold_start, concurrent_batch_size],
outputs=plot_output
)
calc_button.click(
calculate_specific_cost,
inputs=[specific_requests, execution_time, active_cost, flex_cost, num_active_workers, cold_start, concurrent_batch_size],
outputs=cost_table
)
# Launch the app
if __name__ == "__main__":
demo.launch()