Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| import os | |
| from sentence_transformers import SentenceTransformer | |
| import torch | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| # Load knowledge base | |
| with open("knowledge.txt", "r", encoding="utf-8") as f: | |
| knowledge_text = f.read() | |
| chunks = [chunk.strip() for chunk in knowledge_text.split("\n\n") if chunk.strip()] | |
| embedder = SentenceTransformer('all-MiniLM-L6-v2') | |
| chunk_embeddings = embedder.encode(chunks, convert_to_tensor=True) | |
| def get_relevant_context(query, top_k=3): | |
| query_embedding = embedder.encode(query, convert_to_tensor=True) | |
| query_embedding = query_embedding / query_embedding.norm() | |
| norm_chunk_embeddings = chunk_embeddings / chunk_embeddings.norm(dim=1, keepdim=True) | |
| similarities = torch.matmul(norm_chunk_embeddings, query_embedding) | |
| top_k_indices = torch.topk(similarities, k=top_k).indices.cpu().numpy() | |
| context = "\n\n".join([chunks[i]] for i in top_k_indices) | |
| return context | |
| client = InferenceClient("google/gemma-2-2b-it") | |
| cycle_ai_prompt = """ | |
| Cycle-Aware Wellness AI Coach (Strict Enforcement Version) | |
| ========================================================== | |
| Mission: | |
| -------- | |
| You are a compassionate and knowledgeable wellness coach who specializes in fitness aligned with the menstrual cycle and evidence-based contraceptive education. Your mission is to empower women to understand their bodies, support their fitness and reproductive health, and make informed, cycle-aware choices. | |
| You may *only* respond to questions related to: | |
| - Menstrual cycles and hormonal phases | |
| - Cycle-based fitness and wellness programming | |
| - Contraceptive methods and reproductive health education | |
| - Hormonal syndromes or life stages (PCOS, PMDD, menopause, irregular cycles, etc.) | |
| If the user asks something unrelated (e.g., fixing a car, meal prep, unrelated illnesses, tech support), immediately respond with: | |
| "I'm here to help with cycle-based fitness and contraceptive wellness. That question’s outside my scope, but I’d love to support you with anything related to your body, cycle, or health goals!" | |
| You must *never* attempt to answer off-topic requests, even if the user insists or rephrases. Always redirect the conversation back to wellness, hormones, fitness, or reproductive health. | |
| Style and Voice Guidelines: | |
| --------------------------- | |
| - Speak like a friendly, knowledgeable older sister who’s also a certified personal trainer and women’s health educator. | |
| - Be warm, inclusive, and body-positive. Avoid judgment, shame, or clinical coldness. | |
| - Validate the user’s experience before offering guidance (e.g., “That makes total sense—your energy might be shifting in this phase”). | |
| - Encourage autonomy by offering options, not orders. | |
| - Connect advice to real-life impact: how the cycle affects energy, mood, and performance. | |
| When the user asks about workouts, always ask a follow-up: | |
| "What kind of equipment or space do you have access to—like a gym, home weights, or just bodyweight? I’ll tailor a workout for you based on that!" | |
| Final Boundary Rule: | |
| -------------------- | |
| Strictly decline all unrelated questions. Your only purpose is cycle-aware fitness and reproductive wellness coaching. Do not give general medical, tech, cooking, legal, or life advice. | |
| """ | |
| def determine_cycle_phase(start_date_str): | |
| try: | |
| start_date = datetime.strptime(start_date_str, "%Y-%m-%d") | |
| days_since = (datetime.now() - start_date).days % 28 | |
| if days_since < 5: | |
| return "Menstrual Phase", "💗 Time to rest and recover. Gentle movement like stretching or walking is great." | |
| elif days_since < 13: | |
| return "Follicular Phase", "💪 Your energy’s building—go for strength training or cardio!" | |
| elif days_since < 16: | |
| return "Ovulation Phase", "🔥 Peak power! Try high-intensity workouts or social activities." | |
| else: | |
| return "Luteal Phase", "🌙 Wind down. Opt for lighter training, yoga, or bodyweight exercises." | |
| except: | |
| return "Unknown Phase", "Couldn't parse the date. Please use YYYY-MM-DD." | |
| def respond(message, history): | |
| if not isinstance(history, list): | |
| history = [] | |
| messages = [{"role": "system", "content": cycle_ai_prompt}] | |
| for entry in history: | |
| if isinstance(entry, dict): | |
| messages.append(entry) | |
| messages.append({"role": "user", "content": message}) | |
| response = client.chat_completion( | |
| messages, | |
| max_tokens=500, | |
| temperature=0.1 | |
| ) | |
| print("DEBUG RESPONSE:", response) # <-- Add this to inspect | |
| # Now adapt based on actual response | |
| try: | |
| assistant_reply = response['choices'][0]['message']['content'].strip() | |
| except Exception as e: | |
| assistant_reply = f"⚠️ There was an error processing the response: {e}" | |
| new_history = history + [ | |
| {"role": "user", "content": message}, | |
| {"role": "assistant", "content": assistant_reply} | |
| ] | |
| return new_history, new_history, "" | |
| def update_chatbot(user_message, history): | |
| return respond(user_message, history) | |
| def set_user_info(name, age, level, period_start_date, period_end_date): | |
| phase, tip = determine_cycle_phase(period_start_date) | |
| greeting = f"Hi {name}! I'm here to help you with cycle-aware fitness and wellness.\n\nYou’re {age} years old, training at a {level.lower()} level, and your last period started on {period_start_date} and ended on {period_end_date}.\n\nRight now, you’re likely in your **{phase}**. {tip} 💞\n\nAsk me anything about your body, cycle, or contraceptive health!" | |
| return name, [{"role": "assistant", "content": greeting}] | |
| def button_click(question, history): | |
| new_history, updated_history, _ = respond(question, history) | |
| return new_history, updated_history | |
| with gr.Blocks( | |
| css=""" | |
| .gradio-container { | |
| background: linear-gradient(135deg, #F6D365 0%, #FDA085 50%, #FF8FA0 100%); | |
| font-family: 'Quicksand', sans-serif; | |
| } | |
| .message.user { | |
| background-color: lightpink; | |
| border-radius: 20px; | |
| padding: 10px; | |
| margin: 5px; | |
| max-width: 75%; | |
| align-self: flex-end; | |
| } | |
| .message.bot { | |
| background-color: lightcoral; | |
| border-radius: 20px; | |
| padding: 10px; | |
| margin: 5px; | |
| max-width: 75%; | |
| align-self: flex-start; | |
| } | |
| .chat-interface { | |
| background-color: peachpuff; | |
| border-radius: 12px; | |
| padding: 15px; | |
| box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); | |
| } | |
| #banner-image { | |
| background-color: transparent; | |
| margin-bottom: -100px; | |
| } | |
| """, | |
| theme=gr.themes.Soft( | |
| primary_hue="pink", | |
| secondary_hue="orange", | |
| neutral_hue="yellow", | |
| spacing_size="lg", | |
| radius_size="lg", | |
| font=[gr.themes.GoogleFont("Quicksand"), "sans-serif"], | |
| font_mono=[gr.themes.GoogleFont("IBM Plex Mono"), "monospace"] | |
| ) | |
| ) as demo: | |
| gr.Image( | |
| value="/content/Untitled design.png", | |
| show_label=False, | |
| show_share_button = False, | |
| show_download_button = False, | |
| elem_id="banner-image") | |
| name_state = gr.State("") | |
| chat_history = gr.State([]) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Tell me about yourself") | |
| name_input = gr.Textbox(label="Name", placeholder="Your name…") | |
| age_input = gr.Textbox(label="Age", placeholder="Your age…") | |
| level_input = gr.Dropdown(choices=["Beginner", "Intermediate", "Expert"], label="Training Level") | |
| period_start_input = gr.Textbox(label="Last Period Start Date", placeholder="YYYY-MM-DD") | |
| period_end_input = gr.Textbox(label="Last Period End Date", placeholder="YYYY-MM-DD") | |
| set_btn = gr.Button("Set Info") | |
| gr.Markdown("_After the greeting appears, start chatting →_") | |
| with gr.Column(scale=2): | |
| chatbot = gr.Chatbot(label="Chat", type="messages") | |
| user_text = gr.Textbox(placeholder="Ask me something…", label="") | |
| with gr.Row(): | |
| workout_btn = gr.Button("Prompt: Workouts") | |
| contraceptive_btn = gr.Button("Prompt: Contraceptives") | |
| set_btn.click( | |
| fn=set_user_info, | |
| inputs=[name_input, age_input, level_input, period_start_input, period_end_input], | |
| outputs=[name_state, chatbot], | |
| show_progress=False, | |
| ) | |
| user_text.submit( | |
| fn=update_chatbot, | |
| inputs=[user_text, chat_history], | |
| outputs=[chatbot, chat_history, user_text] | |
| ) | |
| workout_btn.click( | |
| fn=lambda history: button_click("What workout routine would you recommend based on my cycle phase?", history), | |
| inputs=[chat_history], | |
| outputs=[chatbot, chat_history] | |
| ) | |
| contraceptive_btn.click( | |
| fn=lambda history: button_click("Can you explain the different contraceptive options and their benefits?", history), | |
| inputs=[chat_history], | |
| outputs=[chatbot, chat_history] | |
| ) | |
| if __name__ == '__main__': | |
| demo.launch() | |