Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@ import pandas as pd
|
|
| 3 |
import numpy as np
|
| 4 |
import plotly.express as px
|
| 5 |
|
| 6 |
-
#
|
| 7 |
aws_instances = {
|
| 8 |
"g4dn.xlarge": {"vcpus": 4, "memory": 16, "gpu": "1x NVIDIA T4", "hourly_rate": 0.526, "gpu_memory": "16GB"},
|
| 9 |
"g4dn.2xlarge": {"vcpus": 8, "memory": 32, "gpu": "1x NVIDIA T4", "hourly_rate": 0.752, "gpu_memory": "16GB"},
|
|
@@ -43,13 +43,12 @@ api_pricing = {
|
|
| 43 |
}
|
| 44 |
|
| 45 |
model_sizes = {
|
| 46 |
-
"Small (7B parameters)": {"memory_required": 14
|
| 47 |
-
"Medium (13B parameters)": {"memory_required": 26
|
| 48 |
-
"Large (70B parameters)": {"memory_required": 140
|
| 49 |
-
"XL (180B parameters)": {"memory_required": 360
|
| 50 |
}
|
| 51 |
|
| 52 |
-
|
| 53 |
def calculate_aws_cost(instance, hours, storage, reserved=False, spot=False, years=1):
|
| 54 |
data = aws_instances[instance]
|
| 55 |
rate = data['hourly_rate']
|
|
@@ -60,8 +59,7 @@ def calculate_aws_cost(instance, hours, storage, reserved=False, spot=False, yea
|
|
| 60 |
rate *= factors.get(years, 0.6)
|
| 61 |
compute = rate * hours
|
| 62 |
storage_cost = storage * 0.10
|
| 63 |
-
return {'
|
| 64 |
-
|
| 65 |
|
| 66 |
def calculate_gcp_cost(instance, hours, storage, reserved=False, spot=False, years=1):
|
| 67 |
data = gcp_instances[instance]
|
|
@@ -73,20 +71,17 @@ def calculate_gcp_cost(instance, hours, storage, reserved=False, spot=False, yea
|
|
| 73 |
rate *= factors.get(years, 0.7)
|
| 74 |
compute = rate * hours
|
| 75 |
storage_cost = storage * 0.04
|
| 76 |
-
return {'
|
| 77 |
-
|
| 78 |
|
| 79 |
def calculate_api_cost(provider, model, input_tokens, output_tokens, api_calls):
|
| 80 |
-
|
| 81 |
-
input_cost =
|
| 82 |
-
output_cost =
|
| 83 |
call_cost = api_calls * 0.0001 if provider == 'TogetherAI' else 0
|
| 84 |
-
|
| 85 |
-
return {'input_cost': input_cost, 'output_cost': output_cost, 'api_call_cost': call_cost, 'total_cost': total}
|
| 86 |
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
result = {}
|
| 90 |
for name, data in instances.items():
|
| 91 |
mem_str = data['gpu_memory']
|
| 92 |
if 'x' in mem_str and not mem_str.startswith(('1x','2x','4x','8x')):
|
|
@@ -97,88 +92,62 @@ def filter_compatible_instances(instances, min_mem):
|
|
| 97 |
else:
|
| 98 |
val = int(mem_str.replace('GB',''))
|
| 99 |
if val >= min_mem:
|
| 100 |
-
|
| 101 |
-
return
|
| 102 |
-
|
| 103 |
|
| 104 |
def generate_cost_comparison(
|
| 105 |
compute_hours, tokens_per_month, input_ratio, api_calls,
|
| 106 |
model_size, storage_gb, reserved_instances, spot_instances, multi_year_commitment
|
| 107 |
):
|
| 108 |
years = int(multi_year_commitment)
|
| 109 |
-
in_tokens = tokens_per_month * (input_ratio/100)
|
| 110 |
out_tokens = tokens_per_month - in_tokens
|
| 111 |
min_mem = model_sizes[model_size]['memory_required']
|
| 112 |
-
aws_comp = filter_compatible_instances(aws_instances, min_mem)
|
| 113 |
-
gcp_comp = filter_compatible_instances(gcp_instances, min_mem)
|
| 114 |
-
results = []
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
| 118 |
if aws_comp:
|
| 119 |
-
|
| 120 |
-
best_aws,
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
aws_html += f'<tr><td>{inst}</td><td>${c:.2f}</td></tr>'
|
| 124 |
-
if c < best_cost:
|
| 125 |
-
best_aws, best_cost = inst, c
|
| 126 |
-
aws_html += '</table>'
|
| 127 |
-
if best_aws:
|
| 128 |
-
results.append({'provider': f'AWS ({best_aws})', 'cost': best_cost, 'type':'Cloud'})
|
| 129 |
-
else:
|
| 130 |
-
aws_html += '<p>No compatible AWS instances.</p>'
|
| 131 |
-
|
| 132 |
-
# GCP table
|
| 133 |
-
gcp_html = '<h3>GCP Compatible Instances</h3>'
|
| 134 |
if gcp_comp:
|
| 135 |
-
|
| 136 |
-
best_gcp,
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
else:
|
| 146 |
-
gcp_html += '<p>No compatible GCP instances.</p>'
|
| 147 |
-
|
| 148 |
-
# API table
|
| 149 |
-
api_html = '<h3>API Options</h3>'
|
| 150 |
-
api_html += '<table width="100%"><tr><th>Provider</th><th>Model</th><th>Total Cost</th></tr>'
|
| 151 |
-
api_costs = {}
|
| 152 |
-
for prov in api_pricing:
|
| 153 |
-
for mdl in api_pricing[prov]:
|
| 154 |
-
cost_data = calculate_api_cost(prov, mdl, in_tokens, out_tokens, api_calls)
|
| 155 |
-
api_costs[(prov,mdl)] = cost_data['total_cost']
|
| 156 |
-
api_html += f'<tr><td>{prov}</td><td>{mdl}</td><td>${cost_data["total_cost"]:.2f}</td></tr>'
|
| 157 |
-
api_html += '</table>'
|
| 158 |
-
best_api = min(api_costs, key=api_costs.get)
|
| 159 |
-
results.append({'provider': f'{best_api[0]} ({best_api[1]})', 'cost': api_costs[best_api], 'type':'API'})
|
| 160 |
-
|
| 161 |
-
# Recommendation
|
| 162 |
-
cheapest = min(results, key=lambda x: x['cost'])
|
| 163 |
-
rec = '<h3>Recommendation</h3>'
|
| 164 |
-
if cheapest['type']=='API':
|
| 165 |
-
rec += f"<p>The API {cheapest['provider']} is cheapest at ${cheapest['cost']:.2f}.</p>"
|
| 166 |
-
else:
|
| 167 |
-
rec += f"<p>The Cloud {cheapest['provider']} is cheapest at ${cheapest['cost']:.2f}.</p>"
|
| 168 |
-
|
| 169 |
-
# Plot
|
| 170 |
df_res = pd.DataFrame(results)
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
def app_function(
|
| 184 |
compute_hours, tokens_per_month, input_ratio, api_calls,
|
|
@@ -189,51 +158,35 @@ def app_function(
|
|
| 189 |
model_size, storage_gb, reserved_instances, spot_instances, multi_year_commitment
|
| 190 |
)
|
| 191 |
|
| 192 |
-
# Gradio
|
| 193 |
def main():
|
| 194 |
with gr.Blocks(title="Cloud Cost Estimator", theme=gr.themes.Soft(primary_hue="indigo")) as demo:
|
| 195 |
gr.HTML("""
|
| 196 |
<div style="text-align:center; margin-bottom:20px;">
|
| 197 |
<h1>Cloud Cost Estimator</h1>
|
| 198 |
-
<p>Compare
|
| 199 |
</div>
|
| 200 |
""")
|
| 201 |
-
|
| 202 |
with gr.Row():
|
| 203 |
with gr.Column(scale=1):
|
| 204 |
-
gr.
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
gr.
|
| 213 |
-
|
| 214 |
-
spot_instances = gr.Checkbox(label="Use Spot/Preemptible Instances", value=False)
|
| 215 |
-
multi_year_commitment = gr.Radio(label="Commitment Period (years)", choices=["1","3"], value="1")
|
| 216 |
-
submit_button = gr.Button("Calculate Costs", variant="primary")
|
| 217 |
-
|
| 218 |
with gr.Column(scale=2):
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
)
|
| 227 |
-
|
| 228 |
-
gr.HTML("""
|
| 229 |
-
<div style="margin-top:30px; border-top:1px solid #e5e7eb; padding-top:20px;">
|
| 230 |
-
<h3>Help & Resources</h3>
|
| 231 |
-
<p><a href="https://aws.amazon.com/ec2/pricing/">AWS EC2 Pricing</a> | <a href="https://cloud.google.com/compute/pricing">GCP Pricing</a></p>
|
| 232 |
-
<p><a href="https://openai.com/pricing">OpenAI API Pricing</a> | <a href="https://www.anthropic.com/api">Anthropic Claude API Pricing</a> | <a href="https://www.together.ai/pricing">TogetherAI Pricing</a></p>
|
| 233 |
-
</div>
|
| 234 |
-
""")
|
| 235 |
-
|
| 236 |
-
demo.launch()
|
| 237 |
|
| 238 |
if __name__ == "__main__":
|
| 239 |
main()
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
import plotly.express as px
|
| 5 |
|
| 6 |
+
# Pricing data
|
| 7 |
aws_instances = {
|
| 8 |
"g4dn.xlarge": {"vcpus": 4, "memory": 16, "gpu": "1x NVIDIA T4", "hourly_rate": 0.526, "gpu_memory": "16GB"},
|
| 9 |
"g4dn.2xlarge": {"vcpus": 8, "memory": 32, "gpu": "1x NVIDIA T4", "hourly_rate": 0.752, "gpu_memory": "16GB"},
|
|
|
|
| 43 |
}
|
| 44 |
|
| 45 |
model_sizes = {
|
| 46 |
+
"Small (7B parameters)": {"memory_required": 14},
|
| 47 |
+
"Medium (13B parameters)": {"memory_required": 26},
|
| 48 |
+
"Large (70B parameters)": {"memory_required": 140},
|
| 49 |
+
"XL (180B parameters)": {"memory_required": 360},
|
| 50 |
}
|
| 51 |
|
|
|
|
| 52 |
def calculate_aws_cost(instance, hours, storage, reserved=False, spot=False, years=1):
|
| 53 |
data = aws_instances[instance]
|
| 54 |
rate = data['hourly_rate']
|
|
|
|
| 59 |
rate *= factors.get(years, 0.6)
|
| 60 |
compute = rate * hours
|
| 61 |
storage_cost = storage * 0.10
|
| 62 |
+
return {'total_cost': compute + storage_cost}
|
|
|
|
| 63 |
|
| 64 |
def calculate_gcp_cost(instance, hours, storage, reserved=False, spot=False, years=1):
|
| 65 |
data = gcp_instances[instance]
|
|
|
|
| 71 |
rate *= factors.get(years, 0.7)
|
| 72 |
compute = rate * hours
|
| 73 |
storage_cost = storage * 0.04
|
| 74 |
+
return {'total_cost': compute + storage_cost}
|
|
|
|
| 75 |
|
| 76 |
def calculate_api_cost(provider, model, input_tokens, output_tokens, api_calls):
|
| 77 |
+
m = api_pricing[provider][model]
|
| 78 |
+
input_cost = input_tokens * m['input_per_1M']
|
| 79 |
+
output_cost = output_tokens * m['output_per_1M']
|
| 80 |
call_cost = api_calls * 0.0001 if provider == 'TogetherAI' else 0
|
| 81 |
+
return {'total_cost': input_cost + output_cost + call_cost}
|
|
|
|
| 82 |
|
| 83 |
+
def filter_compatible(instances, min_mem):
|
| 84 |
+
res = {}
|
|
|
|
| 85 |
for name, data in instances.items():
|
| 86 |
mem_str = data['gpu_memory']
|
| 87 |
if 'x' in mem_str and not mem_str.startswith(('1x','2x','4x','8x')):
|
|
|
|
| 92 |
else:
|
| 93 |
val = int(mem_str.replace('GB',''))
|
| 94 |
if val >= min_mem:
|
| 95 |
+
res[name] = data
|
| 96 |
+
return res
|
|
|
|
| 97 |
|
| 98 |
def generate_cost_comparison(
|
| 99 |
compute_hours, tokens_per_month, input_ratio, api_calls,
|
| 100 |
model_size, storage_gb, reserved_instances, spot_instances, multi_year_commitment
|
| 101 |
):
|
| 102 |
years = int(multi_year_commitment)
|
| 103 |
+
in_tokens = tokens_per_month * (input_ratio / 100)
|
| 104 |
out_tokens = tokens_per_month - in_tokens
|
| 105 |
min_mem = model_sizes[model_size]['memory_required']
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
+
aws_comp = filter_compatible(aws_instances, min_mem)
|
| 108 |
+
gcp_comp = filter_compatible(gcp_instances, min_mem)
|
| 109 |
+
|
| 110 |
+
results = []
|
| 111 |
+
# AWS
|
| 112 |
if aws_comp:
|
| 113 |
+
best_aws = min(aws_comp.keys(), key=lambda x: calculate_aws_cost(x, compute_hours, storage_gb, reserved_instances, spot_instances, years)['total_cost'])
|
| 114 |
+
best_aws_cost = calculate_aws_cost(best_aws, compute_hours, storage_gb, reserved_instances, spot_instances, years)['total_cost']
|
| 115 |
+
results.append({'provider': f'AWS ({best_aws})', 'cost': best_aws_cost, 'type': 'Cloud'})
|
| 116 |
+
# GCP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
if gcp_comp:
|
| 118 |
+
best_gcp = min(gcp_comp.keys(), key=lambda x: calculate_gcp_cost(x, compute_hours, storage_gb, reserved_instances, spot_instances, years)['total_cost'])
|
| 119 |
+
best_gcp_cost = calculate_gcp_cost(best_gcp, compute_hours, storage_gb, reserved_instances, spot_instances, years)['total_cost']
|
| 120 |
+
results.append({'provider': f'GCP ({best_gcp})', 'cost': best_gcp_cost, 'type': 'Cloud'})
|
| 121 |
+
# API (TogetherAI only)
|
| 122 |
+
api_opts = { (prov, m): calculate_api_cost(prov, m, in_tokens, out_tokens, api_calls)['total_cost']
|
| 123 |
+
for prov in api_pricing for m in api_pricing[prov] }
|
| 124 |
+
best_api = min(api_opts, key=api_opts.get)
|
| 125 |
+
results.append({'provider': f'{best_api[0]} ({best_api[1]})', 'cost': api_opts[best_api], 'type': 'API'})
|
| 126 |
+
|
| 127 |
+
# Build bar chart
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
df_res = pd.DataFrame(results)
|
| 129 |
+
aws_name = df_res[df_res['type']=='Cloud']['provider'].iloc[0]
|
| 130 |
+
gcp_name = df_res[df_res['type']=='Cloud']['provider'].iloc[1]
|
| 131 |
+
api_name = df_res[df_res['type']=='API']['provider'].iloc[0]
|
| 132 |
+
|
| 133 |
+
fig = px.bar(
|
| 134 |
+
df_res, x='provider', y='cost', color='provider',
|
| 135 |
+
color_discrete_map={
|
| 136 |
+
aws_name: '#FF9900', # AWS orange
|
| 137 |
+
gcp_name: '#4285F4', # GCP blue
|
| 138 |
+
api_name: '#D62828' # TogetherAI red
|
| 139 |
+
},
|
| 140 |
+
title='Monthly Cost Comparison',
|
| 141 |
+
labels={'provider': 'Provider', 'cost': 'Monthly Cost'}
|
| 142 |
+
)
|
| 143 |
+
fig.update_yaxes(tickprefix='$')
|
| 144 |
+
fig.update_layout(showlegend=False, height=500)
|
| 145 |
|
| 146 |
+
# HTML summary tables omitted for brevity
|
| 147 |
+
html_tables = '<div>'
|
| 148 |
+
# ... you can reinsert your HTML tables here if needed
|
| 149 |
+
html_tables += '</div>'
|
| 150 |
+
return html_tables, fig
|
| 151 |
|
| 152 |
def app_function(
|
| 153 |
compute_hours, tokens_per_month, input_ratio, api_calls,
|
|
|
|
| 158 |
model_size, storage_gb, reserved_instances, spot_instances, multi_year_commitment
|
| 159 |
)
|
| 160 |
|
| 161 |
+
# Gradio UI
|
| 162 |
def main():
|
| 163 |
with gr.Blocks(title="Cloud Cost Estimator", theme=gr.themes.Soft(primary_hue="indigo")) as demo:
|
| 164 |
gr.HTML("""
|
| 165 |
<div style="text-align:center; margin-bottom:20px;">
|
| 166 |
<h1>Cloud Cost Estimator</h1>
|
| 167 |
+
<p>Compare cloud vs API costs</p>
|
| 168 |
</div>
|
| 169 |
""")
|
|
|
|
| 170 |
with gr.Row():
|
| 171 |
with gr.Column(scale=1):
|
| 172 |
+
compute_hours = gr.Slider("Compute Hours per Month", 1, 730, 100)
|
| 173 |
+
tokens_per_month = gr.Slider("Tokens per Month (M)", 1, 1000, 10)
|
| 174 |
+
input_ratio = gr.Slider("Input Ratio (%)", 10, 90, 30)
|
| 175 |
+
api_calls = gr.Slider("API Calls per Month", 100, 1_000_000, 10000, step=100)
|
| 176 |
+
model_size = gr.Dropdown(list(model_sizes.keys()), value="Medium (13B parameters)")
|
| 177 |
+
storage_gb = gr.Slider("Storage (GB)", 10, 1000, 100)
|
| 178 |
+
reserved_instances = gr.Checkbox("Reserved Instances", value=False)
|
| 179 |
+
spot_instances = gr.Checkbox("Spot Instances", value=False)
|
| 180 |
+
multi_year_commitment = gr.Radio(["1","3"], value="1")
|
| 181 |
+
submit = gr.Button("Calculate Costs")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
with gr.Column(scale=2):
|
| 183 |
+
out_html = gr.HTML()
|
| 184 |
+
out_plot = gr.Plot()
|
| 185 |
+
submit.click(app_function,
|
| 186 |
+
inputs=[compute_hours, tokens_per_month, input_ratio, api_calls,
|
| 187 |
+
model_size, storage_gb, reserved_instances, spot_instances, multi_year_commitment],
|
| 188 |
+
outputs=[out_html, out_plot])
|
| 189 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
if __name__ == "__main__":
|
| 192 |
main()
|