|
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 |
|
|
|
|
|
TIMEOUT_SECONDS = 300 |
|
|
|
|
|
graph = create_workflow() |
|
|
|
|
|
logging.basicConfig(level=logging.DEBUG) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
def run_graph(input_message, history, user_details): |
|
def invoke_workflow(q): |
|
try: |
|
|
|
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." |
|
) |
|
|
|
|
|
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 = [ |
|
{"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}) |
|
|
|
|
|
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)) |
|
|
|
|
|
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") |
|
|
|
|
|
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() |
|
|
|
|
|
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) |
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("<strong>FIT.AI - Your Fitness and Wellbeing Coach</strong>") |
|
|
|
with gr.Tabs(): |
|
with gr.Tab("Visualization + Chat"): |
|
|
|
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" |
|
) |
|
|
|
|
|
bmi_chart = gr.Image(label="BMI and Calorie Chart") |
|
|
|
|
|
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]) |
|
|
|
|
|
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) |
|
|