HRV-Insights / app.py
dss107's picture
Update app.py
39d4583 verified
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()