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("FIT.AI - Your Fitness and Wellbeing Coach") 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)