Spaces:
Runtime error
Runtime error
import gradio as gr | |
import matplotlib.pyplot as plt | |
import pandas as np | |
import tempfile | |
from datetime import datetime | |
from langchain_core.messages import HumanMessage | |
from tools import tools | |
from agents import * | |
from config import * | |
from workflow import create_workflow | |
import logging | |
import threading | |
import queue | |
# Global timeout variable | |
TIMEOUT_SECONDS = 300 | |
# Initialize workflow | |
graph = create_workflow() | |
# Configure logging | |
logging.basicConfig(level=logging.DEBUG) | |
logger = logging.getLogger(__name__) | |
# Helper Functions | |
def run_graph(input_message, history, user_details): | |
def invoke_workflow(q): | |
try: | |
# Determine if the query is general or personal | |
if "how" in input_message.lower() or "what" in input_message.lower(): | |
general_query = True if "general" in input_message.lower() else False | |
else: | |
general_query = False | |
system_prompt = ( | |
"You are a fitness and health assistant. " | |
"If the user's query is personal, provide tailored advice based on their details, including BMI and caloric needs. " | |
"If the query is general, respond broadly without relying on personal metrics. " | |
"Encourage the user to ask follow-up questions and maintain a conversational tone." | |
) | |
# Summarize user details | |
user_details_summary = ( | |
f"Name: {user_details.get('name', 'Unknown')}, " | |
f"Age: {user_details.get('age', 'Unknown')}, " | |
f"Gender: {user_details.get('gender', 'Unknown')}, " | |
f"Weight: {user_details.get('weight', 'Unknown')} kg, " | |
f"Height: {user_details.get('height', 'Unknown')} cm, " | |
f"Activity Level: {user_details.get('activity_level', 'Unknown')}" | |
) | |
# Messages for the workflow | |
messages = [ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": f"User details: {user_details_summary}" if not general_query else "General query"}, | |
{"role": "user", "content": input_message} | |
] | |
response = graph.invoke({"messages": messages}) | |
# Extract LLM response | |
llm_response = None | |
for msg in response.get("messages", []): | |
if isinstance(msg, HumanMessage) and msg.name in ["nutritionist", "workout_coach", "general_expert"]: | |
llm_response = msg.content | |
break | |
if llm_response: | |
q.put(llm_response) | |
else: | |
q.put("The workflow did not return a valid response. Please try again.") | |
except Exception as e: | |
q.put(f"An error occurred: {e}") | |
q = queue.Queue() | |
thread = threading.Thread(target=invoke_workflow, args=(q,)) | |
thread.start() | |
thread.join(timeout=TIMEOUT_SECONDS) | |
if thread.is_alive(): | |
return f"The request took longer than {TIMEOUT_SECONDS} seconds and timed out. Please try again." | |
return q.get() | |
def calculate_bmi(height, weight): | |
height_m = height / 100 | |
bmi = weight / (height_m ** 2) | |
if bmi < 18.5: | |
status = "underweight" | |
elif 18.5 <= bmi < 24.9: | |
status = "normal weight" | |
elif 25 <= bmi < 29.9: | |
status = "overweight" | |
else: | |
status = "obese" | |
return bmi, status | |
def visualize_bmi_and_calories(bmi, calories): | |
categories = ["Underweight", "Normal Weight", "Overweight", "Obese"] | |
bmi_values = [18.5, 24.9, 29.9, 40] | |
calorie_range = [1500, 2000, 2500, 3000] | |
fig, ax1 = plt.subplots(figsize=(10, 6)) | |
# BMI Visualization | |
ax1.bar(categories, bmi_values, color=['blue', 'green', 'orange', 'red'], alpha=0.6, label="BMI Ranges") | |
ax1.axhline(y=bmi, color='purple', linestyle='--', linewidth=2, label=f"Your BMI: {bmi:.2f}") | |
ax1.set_ylabel("BMI Value") | |
ax1.set_title("BMI and Caloric Needs Visualization") | |
ax1.legend(loc="upper left") | |
# Calorie Visualization | |
ax2 = ax1.twinx() | |
ax2.plot(categories, calorie_range, 'o-', color='magenta', label="Calorie Ranges") | |
ax2.axhline(y=calories, color='cyan', linestyle='--', linewidth=2, label=f"Your Calorie Needs: {calories:.2f} kcal") | |
ax2.set_ylabel("Calories") | |
ax2.legend(loc="upper right") | |
plt.tight_layout() | |
# Save visualization to a temporary file | |
temp_file = tempfile.NamedTemporaryFile(suffix=".png", delete=False) | |
try: | |
plt.savefig(temp_file.name) | |
finally: | |
plt.close() | |
return temp_file.name | |
def calculate_calories(age, weight, height, activity_level, gender): | |
if gender.lower() == "male": | |
bmr = 10 * weight + 6.25 * height - 5 * age + 5 | |
else: | |
bmr = 10 * weight + 6.25 * height - 5 * age - 161 | |
activity_multipliers = { | |
"sedentary": 1.2, | |
"lightly active": 1.375, | |
"moderately active": 1.55, | |
"very active": 1.725, | |
"extra active": 1.9, | |
} | |
activity_level = activity_level.lower() | |
return bmr * activity_multipliers.get(activity_level, 1.2) | |
# Interface Components | |
with gr.Blocks() as demo: | |
gr.Markdown("<strong>FIT.AI - Your Fitness and Wellbeing Coach</strong>") | |
with gr.Tabs(): | |
with gr.Tab("Visualization + Chat"): | |
# User Input | |
with gr.Row(): | |
user_name = gr.Textbox(placeholder="Enter your name", label="Name") | |
user_age = gr.Number(label="Age (years)", value=25, precision=0) | |
user_gender = gr.Dropdown(choices=["Male", "Female"], label="Gender", value="Male") | |
user_weight = gr.Number(label="Weight (kg)", value=70, precision=1) | |
user_height = gr.Number(label="Height (cm)", value=170, precision=1) | |
activity_level = gr.Dropdown( | |
choices=["Sedentary", "Lightly active", "Moderately active", "Very active", "Extra active"], | |
label="Activity Level", | |
value="Moderately active" | |
) | |
# Visualization Output | |
bmi_chart = gr.Image(label="BMI and Calorie Chart") | |
# Chat Outputs | |
with gr.Row(): | |
chatbot = gr.Chatbot(label="Chat with FIT.AI") | |
text_input = gr.Textbox(placeholder="Type your question here...", label="Your Question") | |
submit_button = gr.Button("Submit") | |
clear_button = gr.Button("Clear Chat") | |
def submit_message(message, history=[]): | |
user_details = { | |
"name": user_name.value, | |
"age": user_age.value, | |
"weight": user_weight.value, | |
"height": user_height.value, | |
"activity_level": activity_level.value, | |
"gender": user_gender.value | |
} | |
bmi, status = calculate_bmi(user_details['height'], user_details['weight']) | |
calories = calculate_calories( | |
user_details['age'], user_details['weight'], user_details['height'], user_details['activity_level'], user_details['gender'] | |
) | |
chart_path = visualize_bmi_and_calories(bmi, calories) | |
user_prompt = ( | |
f"User wants advice on: {message}\n" | |
f"User Details:\n" | |
f"- Name: {user_details['name']}\n" | |
f"- Age: {user_details['age']}\n" | |
f"- Gender: {user_details['gender']}\n" | |
f"- Weight: {user_details['weight']} kg\n" | |
f"- Height: {user_details['height']} cm\n" | |
f"- Activity Level: {user_details['activity_level']}\n" | |
f"- BMI: {bmi:.2f} ({status})\n" | |
f"- Daily Caloric Needs: {calories:.2f} kcal\n" | |
f"\nProvide tailored advice based on these metrics." | |
) | |
response = run_graph(user_prompt, history, user_details) | |
history.append(("User", message)) | |
if isinstance(response, str): | |
history.append(("FIT.AI", response + "\nLet me know if there's anything else you'd like to ask! π")) | |
else: | |
history.append(("FIT.AI", "An unexpected response was received.")) | |
return history, chart_path | |
submit_button.click(submit_message, inputs=[text_input, chatbot], outputs=[chatbot, bmi_chart]) | |
clear_button.click(lambda: ([], ""), inputs=None, outputs=[chatbot, bmi_chart]) | |
# Calculator + Visualization Tab | |
with gr.Tab("Calculator + Visualization"): | |
user_age_calc = gr.Number(label="Age (years)", value=25, precision=0) | |
user_gender_calc = gr.Dropdown(choices=["Male", "Female"], label="Gender", value="Male") | |
user_weight_calc = gr.Number(label="Weight (kg)", value=70, precision=1) | |
user_height_calc = gr.Number(label="Height (cm)", value=170, precision=1) | |
activity_level_calc = gr.Dropdown( | |
choices=["Sedentary", "Lightly active", "Moderately active", "Very active", "Extra active"], | |
label="Activity Level", | |
value="Moderately active" | |
) | |
bmi_output = gr.Label(label="BMI Result") | |
calorie_output = gr.Label(label="Calorie Needs") | |
bmi_chart_calc = gr.Image(label="BMI and Calorie Chart") | |
calculate_button = gr.Button("Calculate") | |
def calculate_metrics(age, weight, height, gender, activity_level): | |
bmi, status = calculate_bmi(height, weight) | |
calories = calculate_calories(age, weight, height, activity_level, gender) | |
chart_path = visualize_bmi_and_calories(bmi, calories) | |
return f"Your BMI is {bmi:.2f}, considered {status}.", f"Daily calorie needs: {calories:.2f} kcal", chart_path | |
calculate_button.click( | |
calculate_metrics, | |
inputs=[user_age_calc, user_weight_calc, user_height_calc, user_gender_calc, activity_level_calc], | |
outputs=[bmi_output, calorie_output, bmi_chart_calc] | |
) | |
demo.launch(share=True) | |