Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import json | |
| import os | |
| # ----------------------------- | |
| # CONFIG | |
| # ----------------------------- | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY") | |
| DEVICE_PARAMS = { | |
| "Apple Watch": [ | |
| "RMSSD", "lnRMSSD", "MeanRR", "HR", "SDNN", | |
| "StressScore", "baseline", "pNN50", "AMo50", "MxDMn" | |
| ], | |
| "Polar": [ | |
| "RMSSD", "SDNN", "MeanRR", "HR", "pNN50", "StressScore" | |
| ], | |
| "Garmin": [ | |
| "RMSSD", "SDNN", "MeanRR", "HR", "StressScore" | |
| ], | |
| "Other": [ | |
| "RMSSD", "SDNN", "MeanRR", "HR", "pNN50", "AMo50", | |
| "StressScore", "baseline", "MxDMn" | |
| ], | |
| } | |
| THRESHOLDS = """ | |
| Use these physiological ranges for interpretation: | |
| - RMSSD: Low <25, Moderate 25β60, High >60 | |
| - SDNN: Low <40, Moderate 40β70, High >70 | |
| - lnRMSSD: Low <3.0, Moderate 3.0β3.9, High >3.9 | |
| - HR: High stress >80 bpm, Normal 55β80, Excellent <55 | |
| - MeanRR: Low <700 (fast heart rate, higher stress), Moderate 700β950 (balanced rhythm), High >950 (good recovery) | |
| - StressScore: Low <30, Moderate 30β60, High >60 | |
| - Baseline: <10 = low trend, 10β12 = stable, >12 = improving | |
| - pNN50: Low <10%, Moderate 10β25%, High >25% | |
| - AMo50: Low <1.5 (good adaptability), High >2.5 (rigidity) | |
| - CV: Low <2, Moderate 2β6, High >6 | |
| """ | |
| # ----------------------------- | |
| # Groq API Call | |
| # ----------------------------- | |
| def call_groq_api(device, data): | |
| prompt = f""" | |
| You are a Welltory-style health assistant who analyzes HRV data with empathy and scientific precision. | |
| Here is the HRV dataset from a {device} device: | |
| {json.dumps(data, indent=2)} | |
| {THRESHOLDS} | |
| Instructions: | |
| 1. Evaluate each parameter relative to its physiological range. | |
| 2. Explain what it means for stress, recovery, and adaptability. | |
| 3. If baseline > 0, describe it as a long-term recovery trend. | |
| 4. Maintain a calm, motivating, yet realistic tone β if HRV is low or stress is high, clearly say so. | |
| 5. Address the user directly using βyouβ and βyourβ. | |
| 6. Do not mention numeric thresholds in the output. | |
| 7. Output in Markdown format with these sections: | |
| **Parameter Insights:** | |
| [List of parameters, each explained clearly β what it measures, what the value means, and what it indicates.] | |
| **Overall Interpretation:** | |
| [A realistic summary of stress, recovery, and physiological state.] | |
| **Actionable Suggestions:** | |
| [3β4 specific, habit-based recommendations to improve HRV and stress recovery.] | |
| If most parameters indicate stress or low HRV, the output tone should sound *concerned but encouraging*, not overly positive. | |
| """ | |
| headers = { | |
| "Authorization": f"Bearer {GROQ_API_KEY}", | |
| "Content-Type": "application/json", | |
| } | |
| body = { | |
| "model": "llama-3.3-70b-versatile", | |
| "messages": [{"role": "user", "content": prompt}], | |
| "temperature": 0.4, # lower temperature = more consistent, realistic tone | |
| } | |
| response = requests.post( | |
| "https://api.groq.com/openai/v1/chat/completions", | |
| headers=headers, | |
| json=body | |
| ) | |
| try: | |
| return response.json()["choices"][0]["message"]["content"] | |
| except Exception as e: | |
| return f"β οΈ Error: {e}\n\nRaw response: {response.text}" | |
| # ----------------------------- | |
| # HRV Analyzer | |
| # ----------------------------- | |
| def analyze_hrv(device, *values): | |
| params = DEVICE_PARAMS.get(device, []) | |
| data = {p: v for p, v in zip(params, values) if v is not None and v != 0} | |
| if not data: | |
| return "β οΈ Please input at least one HRV parameter." | |
| return call_groq_api(device, data) | |
| # ----------------------------- | |
| # Dynamic Parameter Visibility | |
| # ----------------------------- | |
| def update_params(device): | |
| visible_params = DEVICE_PARAMS.get(device, []) | |
| updates = [gr.update(visible=(p in visible_params)) for p in ALL_PARAM_NAMES] | |
| return updates | |
| # ----------------------------- | |
| # BUILD GRADIO UI | |
| # ----------------------------- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## π HRV Wellness Insight β Powered by Groq") | |
| device = gr.Dropdown( | |
| choices=list(DEVICE_PARAMS.keys()), | |
| value="Apple Watch", | |
| label="Select Your Device", | |
| ) | |
| ALL_PARAM_NAMES = sorted({p for params in DEVICE_PARAMS.values() for p in params}) | |
| with gr.Row(): | |
| param_inputs = [ | |
| gr.Number(label=p, visible=(p in DEVICE_PARAMS["Apple Watch"])) | |
| for p in ALL_PARAM_NAMES | |
| ] | |
| analyze_btn = gr.Button("π Analyze HRV") | |
| output = gr.Markdown() | |
| analyze_btn.click(fn=analyze_hrv, inputs=[device] + param_inputs, outputs=output) | |
| device.change(fn=update_params, inputs=device, outputs=param_inputs) | |
| # ----------------------------- | |
| # RUN APP | |
| # ----------------------------- | |
| if __name__ == "__main__": | |
| demo.launch() | |