coaching-app / app.py
kshamaasuresh's picture
Upload 9 files
1acd83f verified
Raw
History Blame Contribute Delete
5.1 kB
"""
app.py
------
Streamlit frontend for the coaching recommendation system.
"""
import os
import streamlit as st
from setup_db import setup
from retrieval import retrieve
from reranker import rerank
# ── Page config ───────────────────────────────────────────────────────────────
st.set_page_config(
page_title="Coaching Assistant",
page_icon="πŸ’ͺ🏻",
layout="centered"
)
# ── One-time DB setup ─────────────────────────────────────────────────────────
# Sets up the SQLite DB if it doesn't already exist.
if not os.path.exists("exercises.db"):
with st.spinner("Setting up exercise database..."):
setup()
# ── Header ────────────────────────────────────────────────────────────────────
st.title("πŸ’ͺ🏻 Coaching Assistant")
st.markdown(
"Describe what you need and get personalised exercise recommendations. "
"You can mention goals, injuries, equipment, or intensity."
)
st.divider()
# ── Example queries ───────────────────────────────────────────────────────────
st.markdown("**Try an example:**")
examples = [
"I have knee pain and need low-impact exercises",
"Explosive drills for a winger",
"Upper body rehab, no weights",
"Beginner core strengthening",
]
cols = st.columns(len(examples))
for i, example in enumerate(examples):
if cols[i].button(example, use_container_width=True):
st.session_state["query"] = example
# ── Query input ───────────────────────────────────────────────────────────────
query = st.text_input(
label="Your query",
placeholder="e.g. low-impact knee rehab for a beginner",
key="query"
)
search_clicked = st.button("Get Recommendations", type="primary", use_container_width=True)
# ── Pipeline ──────────────────────────────────────────────────────────────────
if search_clicked and query.strip():
with st.spinner("Finding the best exercises for you..."):
# Stage 1: BM25 retrieval
candidates = retrieve(query, top_k=15)
if not candidates:
st.warning("No matching exercises found. Try different keywords.")
st.stop()
# Stage 2: LLM re-ranking
try:
results = rerank(query, candidates, top_n=5)
except Exception as e:
st.error(f"Re-ranking failed: {e}")
# Graceful fallback: show top BM25 results without re-ranking
results = [{**c, "reason": ""} for c in candidates[:5]]
# ── Results ───────────────────────────────────────────────────────────────
st.divider()
st.subheader(f"Top recommendations for: *{query}*")
st.caption(f"Retrieved {len(candidates)} candidates β†’ re-ranked to top {len(results)}")
for i, ex in enumerate(results, 1):
# Difficulty badge colour
diff_color = {"beginner": "🟒", "intermediate": "🟑", "advanced": "πŸ”΄"}.get(
ex.get("difficulty", "").lower(), "βšͺ"
)
with st.container(border=True):
col1, col2 = st.columns([3, 1])
with col1:
st.markdown(f"**{i}. {ex['title']}**")
st.caption(ex.get("description", ""))
with col2:
st.markdown(f"{diff_color} {ex.get('difficulty', '').capitalize()}")
st.caption(f"🏷 {ex.get('body_part', '').capitalize()}")
if ex.get("reason"):
st.info(f"πŸ’‘ {ex['reason']}")
# Metadata pills
meta_cols = st.columns(3)
meta_cols[0].markdown(f"**Equipment:** {ex.get('equipment', 'none') or 'none'}")
meta_cols[1].markdown(f"**Intensity:** {ex.get('intensity', 'β€”')}")
meta_cols[2].markdown(f"**Focus:** {ex.get('injury_focus', 'β€”') or 'β€”'}")
elif search_clicked and not query.strip():
st.warning("Please enter a query first.")
# ── Footer ────────────────────────────────────────────────────────────────────
st.divider()
st.caption(
"Pipeline: BM25 keyword retrieval β†’ Mistral-7B re-ranking | "
"Built with Streamlit Β· Deployed on HuggingFace Spaces Β· 100% free"
)