benjosaur's picture
Update README
b383738
"""
Gradio App for EV Utility Function Calculator
Deployed on Hugging Face Spaces
"""
import gradio as gr
import json
import os
from upstash_redis import Redis
# Initialize Redis client
redis = Redis(
url=os.getenv("UPSTASH_REDIS_REST_URL"),
token=os.getenv("UPSTASH_REDIS_REST_TOKEN")
)
# Feature keys in order
FEATURE_KEYS = ["price", "range", "efficiency", "acceleration", "fast_charge", "seat_count"]
# Default coefficients
DEFAULT_COEFFS = {
"price": -0.5,
"range": 0.8,
"efficiency": -0.3,
"acceleration": -0.5,
"fast_charge": 0.6,
"seat_count": 0.4,
}
def get_user_coefficients(user_id: str) -> dict[str, float]:
"""Fetch user coefficients from Redis."""
redis_key = f"params:{user_id}"
data_str = redis.get(redis_key)
if data_str is None:
return DEFAULT_COEFFS
if isinstance(data_str, bytes):
data_str = data_str.decode('utf-8')
data = json.loads(data_str) if isinstance(data_str, str) else data_str
return data.get("coeffs", DEFAULT_COEFFS)
def scale_features(car_features: dict[str, float]) -> list[float]:
"""Scale car features."""
def get_val(key: str) -> float:
val = car_features.get(key)
if val is None:
return 0.0
return float(val)
scaled = [
get_val("price") / 1000,
get_val("range") / 100,
get_val("efficiency") / 10,
get_val("acceleration"),
get_val("fast_charge") / 100,
get_val("seat_count"),
]
return scaled
def calculate_utility_score(car_features: dict[str, float], coeffs: dict[str, float]) -> float:
"""Calculate utility score."""
scaled_features = scale_features(car_features)
coeff_array = [coeffs[key] for key in FEATURE_KEYS]
utility = sum(f * c for f, c in zip(scaled_features, coeff_array))
return utility
def calculate_single_utility(user_id: str, price: float, range_km: float, efficiency: float,
acceleration: float, fast_charge: float, seat_count: int) -> str:
"""Calculate utility for a single car."""
try:
coeffs = get_user_coefficients(user_id)
car_features = {
"price": price,
"range": range_km,
"efficiency": efficiency,
"acceleration": acceleration,
"fast_charge": fast_charge,
"seat_count": seat_count
}
utility = calculate_utility_score(car_features, coeffs)
result = {
"user_id": user_id,
"utility_score": round(utility, 4),
"coefficients_used": {k: round(v, 4) for k, v in coeffs.items()},
"car_features": car_features,
"note": "Using default coefficients" if coeffs == DEFAULT_COEFFS else "Using saved user preferences"
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"error": str(e)}, indent=2)
def find_best_from_list(user_id: str, cars_json: str) -> str:
"""Find the best car from a JSON list."""
try:
cars = json.loads(cars_json)
coeffs = get_user_coefficients(user_id)
best_car = None
best_utility = float('-inf')
all_results = []
for car in cars:
utility = calculate_utility_score(car, coeffs)
car_result = {**car, "utility": round(utility, 4)}
all_results.append(car_result)
if utility > best_utility:
best_utility = utility
best_car = car_result
# Sort by utility descending
all_results.sort(key=lambda x: x["utility"], reverse=True)
result = {
"user_id": user_id,
"best_car": best_car,
"all_cars_ranked": all_results,
"coefficients_used": {k: round(v, 4) for k, v in coeffs.items()},
"note": "Using default coefficients" if coeffs == DEFAULT_COEFFS else "Using saved user preferences"
}
return json.dumps(result, indent=2)
except json.JSONDecodeError:
return json.dumps({"error": "Invalid JSON format"}, indent=2)
except Exception as e:
return json.dumps({"error": str(e)}, indent=2)
# Example cars JSON
example_cars = json.dumps([
{
"name": "Tesla Model 3",
"price": 45000,
"range": 500,
"efficiency": 150,
"acceleration": 6.1,
"fast_charge": 170,
"seat_count": 5
},
{
"name": "Volkswagen ID.4",
"price": 40000,
"range": 420,
"efficiency": 180,
"acceleration": 8.5,
"fast_charge": 125,
"seat_count": 5
},
{
"name": "Hyundai Ioniq 5",
"price": 48000,
"range": 480,
"efficiency": 165,
"acceleration": 7.4,
"fast_charge": 220,
"seat_count": 5
}
], indent=2)
# Create Gradio interface
with gr.Blocks(title="EV Utility Function Calculator") as demo:
gr.Markdown("""
# 🚗 EV Utility Function Calculator
This tool calculates utility scores for electric vehicles based on user preferences.
User preferences are stored in Upstash Redis with their trained coefficients.
**Train your own function here**
https://autofinder.onrender.com
Demo:
https://drive.google.com/file/d/1Z-PoQgvLaEBnhMk8nwrKOnRj8sOKz-QH/view?usp=sharing
Ig Social Media Post:
https://www.instagram.com/p/DRsxbbWCl0m/?img_index=6&igsh=MWoxc3ZrOTM2ZWxhbA==
**Note:** If a user_id is not found, default coefficients will be used.
""")
with gr.Tab("Calculate Single Car Utility"):
gr.Markdown("### Calculate the utility score for a single car")
with gr.Row():
with gr.Column():
user_id_single = gr.Textbox(label="User ID", value="benjo", placeholder="Enter username")
price = gr.Number(label="Price (€)", value=45000)
range_km = gr.Number(label="Range (km)", value=500)
efficiency = gr.Number(label="Efficiency (Wh/km)", value=150)
acceleration = gr.Number(label="0-100km/h (seconds)", value=6.1)
fast_charge = gr.Number(label="Fast Charge (kW)", value=170)
seat_count = gr.Number(label="Seat Count", value=5, precision=0)
with gr.Column():
output_single = gr.Code(label="Result", language="json")
calc_btn = gr.Button("Calculate Utility", variant="primary")
calc_btn.click(
calculate_single_utility,
inputs=[user_id_single, price, range_km, efficiency, acceleration, fast_charge, seat_count],
outputs=output_single
)
with gr.Tab("Find Best Car"):
gr.Markdown("### Find the best car from a list based on user preferences")
with gr.Row():
with gr.Column():
user_id_best = gr.Textbox(label="User ID", value="benjo", placeholder="Enter username")
cars_json = gr.Code(
label="Cars (JSON Array)",
value=example_cars,
language="json",
lines=20
)
with gr.Column():
output_best = gr.Code(label="Result", language="json", lines=25)
find_btn = gr.Button("Find Best Car", variant="primary")
find_btn.click(
find_best_from_list,
inputs=[user_id_best, cars_json],
outputs=output_best
)
gr.Markdown("""
---
## About
This is the web interface for the EV Utility Function MCP Server.
### MCP Server
This tool is also available as an MCP (Model Context Protocol) server that can be used with Claude Desktop
and other MCP-compatible clients.
**Repository:** https://github.com/MJ141592/AutoFinder
### How it works:
1. Users train their preferences through the AutoFinder app
2. Preferences are saved to Upstash Redis as coefficients
3. This tool fetches those coefficients and calculates utility scores
4. Utility = Σ(coefficient × scaled_feature)
""")
if __name__ == "__main__":
demo.launch()