ehagey commited on
Commit
ba77f3f
·
verified ·
1 Parent(s): 7a65a95

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +292 -0
app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ import os
5
+ import hashlib
6
+ from datetime import datetime
7
+ from dotenv import load_dotenv
8
+ from config import MODELS
9
+ load_dotenv(".env", override=True)
10
+
11
+ if 'authenticated' not in st.session_state:
12
+ st.session_state.authenticated = False
13
+ if 'username' not in st.session_state:
14
+ st.session_state.username = None
15
+
16
+ FIXED_USERNAME = os.getenv("USERNAME")
17
+ FIXED_PASSWORD_HASH = hashlib.sha256(os.getenv("PASSWORD").encode()).hexdigest()
18
+
19
+
20
+ st.set_page_config(
21
+ page_title="AI Health Coach",
22
+ page_icon="💪",
23
+ layout="wide",
24
+ )
25
+
26
+ st.markdown("""
27
+ <style>
28
+ .main-header {
29
+ font-size: 2.5rem;
30
+ color: #1E88E5;
31
+ text-align: center;
32
+ margin-bottom: 1rem;
33
+ }
34
+ .workout-card {
35
+ background-color: #f0f2f6;
36
+ border-radius: 10px;
37
+ padding: 20px;
38
+ margin-bottom: 15px;
39
+ }
40
+ .day-header {
41
+ color: #1E88E5;
42
+ font-size: 1.3rem;
43
+ font-weight: bold;
44
+ }
45
+ .exercise-name {
46
+ font-weight: bold;
47
+ color: #333;
48
+ }
49
+ .exercise-detail {
50
+ color: #555;
51
+ margin-left: 10px;
52
+ }
53
+ .section-header {
54
+ font-size: 1.5rem;
55
+ color: #333;
56
+ margin-top: 1rem;
57
+ margin-bottom: 0.5rem;
58
+ }
59
+ .login-container {
60
+ max-width: 500px;
61
+ margin: 0 auto;
62
+ padding: 2rem;
63
+ background-color: #f9f9f9;
64
+ border-radius: 10px;
65
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
66
+ }
67
+ .login-header {
68
+ text-align: center;
69
+ margin-bottom: 1.5rem;
70
+ }
71
+ .logout-btn {
72
+ position: absolute;
73
+ top: 10px;
74
+ right: 10px;
75
+ }
76
+ </style>
77
+ """, unsafe_allow_html=True)
78
+
79
+
80
+ def hash_password(password):
81
+ return hashlib.sha256(password.encode()).hexdigest()
82
+
83
+ def verify_login(username, password):
84
+ hashed_password = hash_password(password)
85
+ return username == FIXED_USERNAME and hashed_password == FIXED_PASSWORD_HASH
86
+
87
+
88
+ def show_login_page():
89
+ st.markdown("<h1 class='main-header'>AI Zach Bouery</h1>", unsafe_allow_html=True)
90
+
91
+ with st.container():
92
+ st.markdown("<div class='login-container'>", unsafe_allow_html=True)
93
+
94
+ st.markdown("<h2 class='login-header'>Login</h2>", unsafe_allow_html=True)
95
+
96
+ username = st.text_input("Username")
97
+ password = st.text_input("Password", type="password")
98
+
99
+ if st.button("Login", use_container_width=True):
100
+ if verify_login(username, password):
101
+ st.session_state.authenticated = True
102
+ st.session_state.username = username
103
+ st.success("Login successful!")
104
+ st.experimental_rerun()
105
+ else:
106
+ st.error("Invalid username or password")
107
+
108
+ st.markdown("</div>", unsafe_allow_html=True)
109
+
110
+ def show_main_app():
111
+ st.markdown("<h1 class='main-header'>AI Zach Bouery </h1>", unsafe_allow_html=True)
112
+
113
+ col1, col2, col3 = st.columns([1, 1, 1])
114
+ with col3:
115
+ st.markdown("<div style='text-align: right;'>", unsafe_allow_html=True)
116
+ if st.button("Logout"):
117
+ st.session_state.authenticated = False
118
+ st.session_state.username = None
119
+ st.experimental_rerun()
120
+ st.markdown(f"Logged in as: **{st.session_state.username}**")
121
+ st.markdown("</div>", unsafe_allow_html=True)
122
+
123
+ if 'openrouter_config' not in st.session_state:
124
+ st.session_state.openrouter_config = {"api_key": os.getenv('OPENROUTER_API_KEY')}
125
+ if 'workout_plan' not in st.session_state:
126
+ st.session_state.workout_plan = None
127
+ if 'generation_time' not in st.session_state:
128
+ st.session_state.generation_time = None
129
+ if 'model_used' not in st.session_state:
130
+ st.session_state.model_used = None
131
+
132
+ with st.sidebar:
133
+ st.header("Configuration")
134
+
135
+ openrouter_api_key = os.getenv("OpenrouterAPIKey")
136
+
137
+ st.session_state.openrouter_config["api_key"] = openrouter_api_key
138
+
139
+ st.subheader("Model Selection")
140
+ model_options = list(MODELS.keys())
141
+ selected_model = st.selectbox("Choose an LLM", model_options)
142
+
143
+ st.subheader("User Information")
144
+ user_age = st.number_input("Age", min_value=18, max_value=100, value=30)
145
+ user_weight = st.number_input("Weight (kg)", min_value=30, max_value=250, value=70)
146
+ user_height = st.number_input("Height (cm)", min_value=100, max_value=250, value=170)
147
+
148
+ fitness_level = st.selectbox(
149
+ "Fitness Level",
150
+ options=["Beginner", "Intermediate", "Advanced"]
151
+ )
152
+ health_conditions = st.multiselect(
153
+ "Health Conditions (if any)",
154
+ ["None", "High Blood Pressure", "Diabetes", "Asthma", "Joint Pain", "Back Pain", "Heart Condition", "Other"]
155
+ )
156
+
157
+ time_available = st.text_input("Time Available Per Day (minutes)")
158
+
159
+ col1, col2 = st.columns([1, 1])
160
+
161
+ with col1:
162
+ st.markdown("<div class='section-header'>Workout Goals</div>", unsafe_allow_html=True)
163
+ primary_goal = st.selectbox(
164
+ "Primary Goal",
165
+ ["Weight Loss", "Muscle Building", "Cardiovascular Health", "Flexibility & Mobility", "General Fitness"]
166
+ )
167
+ secondary_goals = st.multiselect(
168
+ "Secondary Goals (Optional)",
169
+ ["Stress Reduction", "Better Sleep", "Increased Energy", "Improved Posture", "Core Strength", "Sport-Specific Training"]
170
+ )
171
+ equipment_available = st.multiselect(
172
+ "Equipment Available",
173
+ ["None (Bodyweight only)", "Dumbbells", "Resistance Bands", "Kettlebells", "Pull-up Bar", "Bench", "Full Gym Access"]
174
+ )
175
+ workout_days = st.text_input("Days Per Week", "3")
176
+ workout_location = st.radio("Workout Location", ["Home", "Gym"])
177
+
178
+ with col2:
179
+ st.markdown("<div class='section-header'>Customize Prompt</div>", unsafe_allow_html=True)
180
+
181
+ default_prompt = f"""
182
+ You are an expert fitness coach creating a personalized weekly workout plan.
183
+ Primary goal: {primary_goal}
184
+ Secondary goals: {', '.join(secondary_goals) if secondary_goals else 'None'}
185
+ Fitness level: {fitness_level}
186
+ Age: {user_age}
187
+ Weight: {user_weight} kg
188
+ Height: {user_height} cm
189
+ Health conditions: {', '.join(health_conditions) if health_conditions else 'None'}
190
+ Time available: {time_available} minutes per session
191
+ Workout days: {workout_days} days per week
192
+ Equipment: {', '.join(equipment_available) if equipment_available else 'None'}
193
+ Location: {workout_location}
194
+
195
+ Create a detailed weekly workout plan with these requirements:
196
+ 1. Include {workout_days} workout days with rest days appropriately spaced
197
+ 2. Each workout should be completable within {time_available} minutes
198
+ 3. Include general warm-up, then day-specific warmup
199
+ 4. Include general cool-down recommendations and warm-up sets for each exercise
200
+ 5. Provide specific exercises with sets, reps, rest periods, and recommended RPE
201
+ 6. Use RPE and RIR based approach
202
+ 7. Use science based approach for exercise selection and progression
203
+ 8. Do not recommend redundant exercises (ie, 2 exercsies that target the same muscle, at the exact same angle and ROM)
204
+ 9. Include progression tips
205
+ 10. Do not give a specific day for core, recommend to do core exercises on every other day (max 3 exercises)
206
+ 11. All muscle groups need to be targeted 2x per week
207
+ 12. Format the response in a clear, organized way with days of the week as headers
208
+ 13. Include a brief explanation of why this plan suits the user's goals and fitness level
209
+ 14. Take into consideration that the user is at {fitness_level} fitness level
210
+ """
211
+
212
+ prompt_text = st.text_area("Customize Prompt", default_prompt, height=400)
213
+
214
+ if st.button("Generate Workout Plan", type="primary"):
215
+ if not st.session_state.openrouter_config["api_key"]:
216
+ st.error("Please enter your OpenRouter API key.")
217
+ else:
218
+ with st.spinner("Generating your personalized workout plan..."):
219
+ try:
220
+ model_config = MODELS[selected_model]
221
+
222
+ headers = {
223
+ "Authorization": f"Bearer {st.session_state.openrouter_config['api_key']}",
224
+ "Content-Type": "application/json"
225
+ }
226
+
227
+ payload = {
228
+ "model": model_config["model_id"],
229
+ "messages": [
230
+ {"role": "system", "content": "You are an expert fitness coach specializing in creating personalized workout plans."},
231
+ {"role": "user", "content": prompt_text}
232
+ ],
233
+ "temperature": 0.7,
234
+ "max_tokens": 4000
235
+ }
236
+
237
+ response = requests.post(
238
+ "https://openrouter.ai/api/v1/chat/completions",
239
+ headers=headers,
240
+ data=json.dumps(payload)
241
+ )
242
+
243
+ if response.status_code == 200:
244
+ response_data = response.json()
245
+
246
+
247
+ workout_plan = response_data["choices"][0]["message"]["content"]
248
+
249
+ st.session_state.workout_plan = workout_plan
250
+ st.session_state.generation_time = datetime.now().strftime("%Y-%m-%d %H:%M")
251
+ st.session_state.model_used = selected_model
252
+
253
+ st.success("Workout plan generated successfully!")
254
+ else:
255
+ error_details = f"Status Code: {response.status_code}"
256
+ try:
257
+ error_json = response.json()
258
+ error_details += f"\nError: {json.dumps(error_json, indent=2)}"
259
+ except:
260
+ error_details += f"\nResponse Text: {response.text}"
261
+
262
+ st.error(f"API Error: {error_details}")
263
+
264
+ except Exception as e:
265
+ st.error(f"An error occurred: {str(e)}")
266
+ import traceback
267
+ st.code(traceback.format_exc())
268
+
269
+
270
+ if st.session_state.workout_plan:
271
+ st.markdown("---")
272
+ st.markdown("<div class='section-header'>Your Personalized Workout Plan</div>", unsafe_allow_html=True)
273
+
274
+ col1, col2 = st.columns([1, 1])
275
+ with col1:
276
+ st.info(f"Generated on: {st.session_state.generation_time}")
277
+ with col2:
278
+ st.info(f"Model: {st.session_state.model_used}")
279
+ st.markdown(st.session_state.workout_plan)
280
+
281
+ st.markdown("---")
282
+ st.markdown("© 2025 AI Health Coach | Powered by OpenRouter")
283
+
284
+
285
+ def main():
286
+ if st.session_state.authenticated:
287
+ show_main_app()
288
+ else:
289
+ show_login_page()
290
+
291
+ if __name__ == "__main__":
292
+ main()