Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import plotly.graph_objects as go
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
|
6 |
+
def calculate_runpod_cost(
|
7 |
+
requests_per_hour,
|
8 |
+
execution_time_per_request,
|
9 |
+
active_cost_per_second,
|
10 |
+
flex_cost_per_second,
|
11 |
+
cold_start_penalty=30,
|
12 |
+
active_request_percentage=0.5
|
13 |
+
):
|
14 |
+
# Calculate monthly requests
|
15 |
+
monthly_requests = requests_per_hour * 24 * 30
|
16 |
+
|
17 |
+
# Split requests between active and flex workers
|
18 |
+
active_requests = monthly_requests * active_request_percentage
|
19 |
+
flex_requests = monthly_requests * (1 - active_request_percentage)
|
20 |
+
|
21 |
+
# Calculate active worker cost
|
22 |
+
active_cost = active_requests * execution_time_per_request * active_cost_per_second
|
23 |
+
|
24 |
+
# Calculate flex worker cost (including cold start penalty)
|
25 |
+
flex_cost = flex_requests * (execution_time_per_request + cold_start_penalty) * flex_cost_per_second
|
26 |
+
|
27 |
+
# Total monthly cost
|
28 |
+
total_monthly_cost = active_cost + flex_cost
|
29 |
+
|
30 |
+
return {
|
31 |
+
"monthly_requests": monthly_requests,
|
32 |
+
"active_requests": active_requests,
|
33 |
+
"flex_requests": flex_requests,
|
34 |
+
"active_cost": active_cost,
|
35 |
+
"flex_cost": flex_cost,
|
36 |
+
"total_monthly_cost": total_monthly_cost
|
37 |
+
}
|
38 |
+
|
39 |
+
def generate_cost_projection(
|
40 |
+
min_requests,
|
41 |
+
max_requests,
|
42 |
+
execution_time,
|
43 |
+
active_cost,
|
44 |
+
flex_cost,
|
45 |
+
active_percentage,
|
46 |
+
cold_start
|
47 |
+
):
|
48 |
+
# Generate data for different request volumes
|
49 |
+
request_volumes = np.linspace(min_requests, max_requests, 20)
|
50 |
+
results = []
|
51 |
+
|
52 |
+
for req_vol in request_volumes:
|
53 |
+
result = calculate_runpod_cost(
|
54 |
+
req_vol,
|
55 |
+
execution_time,
|
56 |
+
active_cost,
|
57 |
+
flex_cost,
|
58 |
+
cold_start,
|
59 |
+
active_percentage/100
|
60 |
+
)
|
61 |
+
results.append({
|
62 |
+
'requests_per_hour': req_vol,
|
63 |
+
'monthly_requests': result['monthly_requests'],
|
64 |
+
'active_cost': result['active_cost'],
|
65 |
+
'flex_cost': result['flex_cost'],
|
66 |
+
'total_cost': result['total_monthly_cost']
|
67 |
+
})
|
68 |
+
|
69 |
+
df = pd.DataFrame(results)
|
70 |
+
|
71 |
+
# Create plotly figure
|
72 |
+
fig = go.Figure()
|
73 |
+
fig.add_trace(go.Scatter(
|
74 |
+
x=df['requests_per_hour'],
|
75 |
+
y=df['active_cost'],
|
76 |
+
name='Active Worker Cost',
|
77 |
+
line=dict(color='blue')
|
78 |
+
))
|
79 |
+
fig.add_trace(go.Scatter(
|
80 |
+
x=df['requests_per_hour'],
|
81 |
+
y=df['flex_cost'],
|
82 |
+
name='Flex Worker Cost',
|
83 |
+
line=dict(color='orange')
|
84 |
+
))
|
85 |
+
fig.add_trace(go.Scatter(
|
86 |
+
x=df['requests_per_hour'],
|
87 |
+
y=df['total_cost'],
|
88 |
+
name='Total Cost',
|
89 |
+
line=dict(color='green', width=3)
|
90 |
+
))
|
91 |
+
|
92 |
+
fig.update_layout(
|
93 |
+
title='RunPod Serverless Monthly Cost Projection',
|
94 |
+
xaxis_title='Requests per Hour',
|
95 |
+
yaxis_title='Monthly Cost ($)',
|
96 |
+
hovermode='x unified'
|
97 |
+
)
|
98 |
+
|
99 |
+
return fig
|
100 |
+
|
101 |
+
def calculate_specific_cost(
|
102 |
+
requests,
|
103 |
+
execution_time,
|
104 |
+
active_cost,
|
105 |
+
flex_cost,
|
106 |
+
active_percentage,
|
107 |
+
cold_start
|
108 |
+
):
|
109 |
+
result = calculate_runpod_cost(
|
110 |
+
requests,
|
111 |
+
execution_time,
|
112 |
+
active_cost,
|
113 |
+
flex_cost,
|
114 |
+
cold_start,
|
115 |
+
active_percentage/100
|
116 |
+
)
|
117 |
+
|
118 |
+
# Format the output HTML for the table
|
119 |
+
html = f"""
|
120 |
+
<table style="width:100%; border-collapse: collapse;">
|
121 |
+
<tr style="background-color: #f2f2f2;">
|
122 |
+
<th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Metric</th>
|
123 |
+
<th style="padding: 12px; text-align: right; border: 1px solid #ddd;">Value</th>
|
124 |
+
</tr>
|
125 |
+
<tr>
|
126 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Monthly Requests</td>
|
127 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['monthly_requests']:,.0f}</td>
|
128 |
+
</tr>
|
129 |
+
<tr>
|
130 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Cost</td>
|
131 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['active_cost']:,.2f}</td>
|
132 |
+
</tr>
|
133 |
+
<tr>
|
134 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Flex Worker Cost</td>
|
135 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['flex_cost']:,.2f}</td>
|
136 |
+
</tr>
|
137 |
+
<tr style="font-weight: bold;">
|
138 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Total Monthly Cost</td>
|
139 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['total_monthly_cost']:,.2f}</td>
|
140 |
+
</tr>
|
141 |
+
</table>
|
142 |
+
"""
|
143 |
+
|
144 |
+
return html
|
145 |
+
|
146 |
+
theme = gr.themes.Default(
|
147 |
+
primary_hue=gr.themes.colors.red,
|
148 |
+
secondary_hue=gr.themes.colors.red,
|
149 |
+
neutral_hue=gr.themes.colors.slate,
|
150 |
+
)
|
151 |
+
|
152 |
+
# Create Gradio interface
|
153 |
+
with gr.Blocks(title="RunPod Serverless Cost Estimator", theme=theme) as demo:
|
154 |
+
gr.Markdown("# RunPod Serverless Cost Estimator")
|
155 |
+
gr.Markdown("Estimate your monthly RunPod Serverless costs based on request volume and execution time")
|
156 |
+
|
157 |
+
with gr.Row():
|
158 |
+
with gr.Column(scale=1):
|
159 |
+
min_requests = gr.Slider(minimum=100, maximum=50000, value=1000, step=1000, label="Minimum Requests/Hour")
|
160 |
+
max_requests = gr.Slider(minimum=1000, maximum=100000, value=20000, step=1000, label="Maximum Requests/Hour")
|
161 |
+
execution_time = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Execution Time per Request (seconds)")
|
162 |
+
active_cost = gr.Number(value=0.00019, label="Active Worker Cost ($/second)", precision=5)
|
163 |
+
flex_cost = gr.Number(value=0.00031, label="Flex Worker Cost ($/second)", precision=5)
|
164 |
+
active_percentage = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="% Requests on Active Workers")
|
165 |
+
cold_start = gr.Slider(minimum=0, maximum=60, value=30, step=1, label="Cold Start Penalty (seconds)")
|
166 |
+
|
167 |
+
plot_button = gr.Button("Generate Cost Projection")
|
168 |
+
|
169 |
+
with gr.Column(scale=2):
|
170 |
+
plot_output = gr.Plot(label="Cost Projection")
|
171 |
+
|
172 |
+
gr.Markdown("## Cost Breakdown for Specific Request Volume")
|
173 |
+
|
174 |
+
with gr.Row():
|
175 |
+
with gr.Column(scale=1):
|
176 |
+
specific_requests = gr.Number(value=10000, label="Requests per Hour", precision=0)
|
177 |
+
calc_button = gr.Button("Calculate Cost")
|
178 |
+
|
179 |
+
with gr.Column(scale=2):
|
180 |
+
cost_table = gr.HTML()
|
181 |
+
|
182 |
+
plot_button.click(
|
183 |
+
generate_cost_projection,
|
184 |
+
inputs=[min_requests, max_requests, execution_time, active_cost, flex_cost, active_percentage, cold_start],
|
185 |
+
outputs=plot_output
|
186 |
+
)
|
187 |
+
|
188 |
+
calc_button.click(
|
189 |
+
calculate_specific_cost,
|
190 |
+
inputs=[specific_requests, execution_time, active_cost, flex_cost, active_percentage, cold_start],
|
191 |
+
outputs=cost_table
|
192 |
+
)
|
193 |
+
|
194 |
+
# Launch the app
|
195 |
+
if __name__ == "__main__":
|
196 |
+
demo.launch()
|