File size: 12,259 Bytes
ada5f60
e60000f
c9ad2ad
7493dcd
182d8f2
c9ad2ad
aadba9b
2037190
5106689
e60000f
4cdd98c
65b3dbc
 
2037190
3ea51d3
4cdd98c
5106689
4cdd98c
3ea51d3
5106689
 
4cdd98c
3ea51d3
65b3dbc
b91cf21
 
4d17783
3c89a84
4d17783
65b3dbc
 
 
3c89a84
 
65b3dbc
 
b91cf21
65b3dbc
 
3c89a84
 
65b3dbc
 
b91cf21
65b3dbc
 
3c89a84
 
65b3dbc
 
3ea51d3
 
 
 
 
 
 
b91cf21
65b3dbc
b91cf21
3ea51d3
 
65b3dbc
2bbc5e5
4cdd98c
859024f
f1402fa
65b3dbc
859024f
 
 
 
 
 
 
65b3dbc
 
 
5106689
 
 
 
 
859024f
4cdd98c
65b3dbc
f1402fa
5106689
8b468d2
5106689
 
4cdd98c
 
 
 
 
 
 
 
 
3ea51d3
65b3dbc
3ea51d3
 
 
 
 
4cdd98c
 
 
 
 
 
 
65b3dbc
3ea51d3
4cdd98c
 
 
 
5106689
f6fdf70
65b3dbc
f6fdf70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ea51d3
 
 
 
 
 
f6fdf70
 
 
 
3ea51d3
f6fdf70
 
 
5106689
ef779bc
1ec9aec
3ea51d3
 
 
 
 
 
1ec9aec
289182b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b74593
289182b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b74593
289182b
 
 
 
 
 
1ec9aec
3ea51d3
 
4cdd98c
1ec9aec
4cdd98c
 
 
3ea51d3
 
 
 
 
 
 
 
 
 
 
 
7b74593
4cdd98c
3ea51d3
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
import streamlit as st
import google.generativeai as genai
from PIL import Image
import requests

# Configure Google Generative AI library with an API key
genai.configure(api_key="AIzaSyBuQHthxfR0t-3RtZC44YxTBJWN4roz-O4")

# API configurations for Dobby
DOBBY_API_URL = "https://api.fireworks.ai/inference/v1/chat/completions"
DOBBY_API_KEY = "fw_3ZjtsywUGddwa1wGY4VvB3eW"
DOBBY_LEASHED_MODEL = "accounts/sentientfoundation/models/dobby-mini-leashed-llama-3-1-8b#accounts/sentientfoundation/deployments/22e7b3fd"
DOBBY_UNHINGED_MODEL = "accounts/sentientfoundation/models/dobby-mini-unhinged-llama-3-1-8b#accounts/sentientfoundation/deployments/81e155fc"

# Initialize session state for tracking meals and user info
if "meal_data" not in st.session_state:
    st.session_state.meal_data = {"Breakfast": {}, "Lunch": {}, "Dinner": {}}
if "daily_totals" not in st.session_state:
    st.session_state.daily_totals = {"Calories": 0, "Protein": 0, "Carbs": 0, "Fat": 0}
if "user_info" not in st.session_state:
    st.session_state.user_info = {}


st.markdown("""
    <style>
        .stApp {
            background-color: #1a1a1a; /* Dark black background */
            color: #FFFFFF;  /* Set text color to white for readability */
            font-family: 'Inter', sans-serif; /* Inter font */
        }
        .title {
            font-size: 36px;
            font-weight: bold;
            color: #FFFFFF; /* White text for title */
            text-align: center;
            margin-bottom: 20px;
        }
        .subtitle {
            font-size: 20px;
            font-weight: 400;
            color: #CCCCCC; /* Light grey for subtitle */
            text-align: center;
            margin-bottom: 40px;
        }
        .header {
            font-size: 24px;
            font-weight: bold;
            color: #FFFFFF; /* White text for headers */
            margin-bottom: 10px;
        }
        .button {
            background-color: #4CAF50; /* Green button */
            color: white;
            font-size: 16px;
            padding: 10px 20px;
            border-radius: 5px;
        }
    </style>
""", unsafe_allow_html=True)



st.markdown('<div class="title">Calorie Bro</div>', unsafe_allow_html=True)
st.markdown('<div class="subtitle">Your personal AI image calorie tracker, powered by Sentient Dobby AI. Upload you breakfast, lunch and dinner, write your workout for the day and dare to get some 18+ feedback!</div>', unsafe_allow_html=True)

# Step 1: User inputs weight (in pounds), goal, and Dobby tone
if not st.session_state.user_info:
    st.markdown('<div class="header">Step 1: Set Your Preferences</div>', unsafe_allow_html=True)
    weight = st.number_input(
        "Enter your weight (lbs):", 
        min_value=50,  # Minimum weight in pounds (~30 kg)
        max_value=1000,  # Maximum weight in pounds (~300 kg)
        step=1, 
        value=175
    )
    goal = st.selectbox("Select your goal:", ["Lose Weight", "Maintain Weight", "Gain Weight"])
    tone = "Sarcastic (Unhinged)"  # Default tone
    if st.button("Submit", help="Save your preferences to start tracking meals."):
        st.session_state.user_info = {
            "Weight": weight,
            "Goal": goal,
            "Tone": tone
        }
        st.success("Your preferences have been saved!")

# Step 2: Meal tracking
if st.session_state.user_info:
    meal = st.selectbox("Select Meal:", ["Breakfast", "Lunch", "Dinner"])
    uploaded_file = st.file_uploader(f"Upload your {meal} image", type=["jpg", "jpeg", "png"])

    # Functions for analysis and comments
    def analyze_food_with_gemini(input_prompt, image_data):
        model = genai.GenerativeModel("gemini-1.5-pro-latest")
        response = model.generate_content([input_prompt, image_data[0]])
        return response.text

    def prepare_image_data(uploaded_file):
        bytes_data = uploaded_file.getvalue()
        return [{"mime_type": uploaded_file.type, "data": bytes_data}]

    def get_dobby_comment(food_name, total_calories, total_protein, total_carbs, total_fat, meal, goal, tone):
        selected_model = DOBBY_UNHINGED_MODEL  # Default tone is sarcastic
        dobby_prompt = f"""
        The user had {meal}: {food_name}.
        Nutritional data: {total_calories} kcal, {total_protein} g protein, {total_carbs} g carbs, {total_fat} g fat.
        The user's goal is to {goal.lower()}. Provide feedback for this meal.
        """
        response = requests.post(
            DOBBY_API_URL,
            headers={
                "Authorization": f"Bearer {DOBBY_API_KEY}",
                "Content-Type": "application/json",
            },
            json={
                "model": selected_model,
                "messages": [{"role": "user", "content": dobby_prompt}],
            },
        )
        return response.json().get("choices", [{}])[0].get("message", {}).get("content", "No response")

    # Process uploaded meal image
    if uploaded_file:
        st.image(uploaded_file, caption=f"Uploaded {meal} Image", use_container_width=True)
        with st.spinner("Analyzing the image..."):
            try:
                # Prepare image data and analyze with Gemini
                image_data = prepare_image_data(uploaded_file)
                input_prompt = """
                You are an expert nutritionist analyzing the food items in the image.
                Start by naming the meal based on the image, identify all ingredients, and estimate calories for each ingredient.
                Summarize the total calories, protein, carbs, and fat. Provide data in this format:
                Meal Name: [Name of the meal]
                Total Calories: X kcal
                Protein: X g
                Carbs: X g
                Fat: X g
                say nothing else
                """
                analysis = analyze_food_with_gemini(input_prompt, image_data)

                # Extract analysis data
                lines = analysis.split("\n")
                food_name = lines[0].replace("Meal Name: ", "").strip()
                total_calories = int(lines[1].replace("Total Calories: ", "").replace("kcal", "").strip())
                total_protein = int(lines[2].replace("Protein: ", "").replace("g", "").strip())
                total_carbs = int(lines[3].replace("Carbs: ", "").replace("g", "").strip())
                total_fat = int(lines[4].replace("Fat: ", "").replace("g", "").strip())

                # Save data for this meal
                st.session_state.meal_data[meal] = {
                    "Food Name": food_name,
                    "Calories": total_calories,
                    "Protein": total_protein,
                    "Carbs": total_carbs,
                    "Fat": total_fat,
                }

                # Update daily totals
                st.session_state.daily_totals["Calories"] += total_calories
                st.session_state.daily_totals["Protein"] += total_protein
                st.session_state.daily_totals["Carbs"] += total_carbs
                st.session_state.daily_totals["Fat"] += total_fat

                # Get Dobby's feedback
                dobby_comment = get_dobby_comment(
                    food_name, total_calories, total_protein, total_carbs, total_fat, meal,
                    st.session_state.user_info["Goal"], st.session_state.user_info["Tone"]
                )

                # Display results in the desired format
                st.write(
                    f"**{food_name}: {total_calories} kcal, {total_protein}g of Protein, {total_carbs}g of Carbs, {total_fat}g of Fat**"
                )
                st.write(dobby_comment)

            except Exception as e:
                st.error(f"Error processing the image: {e}")

# Step 3: Workout tracking and analysis
if all(st.session_state.meal_data[meal] for meal in ["Breakfast", "Lunch", "Dinner"]):
    st.subheader("Step 3: Log Your Workout")
    workout_input = st.text_area(
        "Describe your workout or activity today (e.g., 'I walked for 30 minutes and biked for 1 hour'):",
        placeholder="Enter your workout activities here..."
    )

    if st.button("Analyze Workout"):
        if workout_input.strip() == "":
            st.error("Please describe your workout before analyzing!")
        else:
            with st.spinner("Analyzing your workout..."):
                try:
                    # Gemini prompt for workout analysis
                    workout_prompt = f"""
                    You are an expert fitness tracker. The user did the following activities today:
                    {workout_input}
                    Calculate the total calories burned based on the description. 
                    Provide data in this format: "X calories burned by [activity descriptions]." Say nothing else.
                    """
                    # Analyze workout using Gemini for text
                    model = genai.GenerativeModel("gemini-1.5-pro-latest")
                    workout_analysis = model.generate_content([workout_prompt]).text.strip()
                    st.session_state.daily_totals["Workout"] = workout_analysis
    
                    # Dobby's feedback on the workout
                    dobby_workout_prompt = f"""
                    The user performed the following workout activities: {workout_input}.
                    Based on the analysis, {workout_analysis}.
                    Provide sarcastic or motivational feedback based on the user's goal to {st.session_state.user_info["Goal"].lower()}.
                    """
                    dobby_workout_comment = requests.post(
                        DOBBY_API_URL,
                        headers={
                            "Authorization": f"Bearer {DOBBY_API_KEY}",
                            "Content-Type": "application/json",
                        },
                        json={
                            "model": DOBBY_UNHINGED_MODEL,  # Default to unhinged tone
                            "messages": [{"role": "user", "content": dobby_workout_prompt}],
                        },
                    ).json().get("choices", [{}])[0].get("message", {}).get("content", "No response")
                    st.session_state.daily_totals["Workout Comment"] = dobby_workout_comment
    
                    # Display workout results
                    st.write(f"**{workout_analysis}**")
                    st.write(dobby_workout_comment)
                except Exception as e:
                    st.error(f"Error processing workout: {e}")


    # Step 4: Daily Summary
    if "Workout" in st.session_state.daily_totals:
        st.subheader("Daily Summary")
        st.write(f"**Total Calories Consumed:** {st.session_state.daily_totals['Calories']} kcal")
        st.write(f"**Total Protein:** {st.session_state.daily_totals['Protein']} g")
        st.write(f"**Total Carbs:** {st.session_state.daily_totals['Carbs']} g")
        st.write(f"**Total Fat:** {st.session_state.daily_totals['Fat']} g")
        st.write(f"**Workout Summary:** {st.session_state.daily_totals['Workout']}")

        # Final Dobby comment for the entire day
        final_prompt = f"""
        The user had the following meals today:
        Breakfast: {st.session_state.meal_data['Breakfast']['Food Name']}
        Lunch: {st.session_state.meal_data['Lunch']['Food Name']}
        Dinner: {st.session_state.meal_data['Dinner']['Food Name']}
        Total daily intake: {st.session_state.daily_totals['Calories']} kcal, 
        {st.session_state.daily_totals['Protein']} g protein, {st.session_state.daily_totals['Carbs']} g carbs, 
        {st.session_state.daily_totals['Fat']} g fat.
        They also did the following workout: {st.session_state.daily_totals['Workout']}
        Provide a summary of their day, considering both diet AND workout, and feedback for improvement.
        """
        final_comment = get_dobby_comment(
            "", st.session_state.daily_totals["Calories"], st.session_state.daily_totals["Protein"],
            st.session_state.daily_totals["Carbs"], st.session_state.daily_totals["Fat"], "Daily Summary",
            st.session_state.user_info["Goal"], st.session_state.user_info["Tone"]
        )
        st.write(final_comment)