""" VibeFinder โ€” Streamlit Web Interface """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import streamlit as st from src.recommender import load_songs, recommend_songs from src.agentic_workflow import RecommendationAgent from src.recommender import Recommender from src.reliability_testing import ReliabilityTester from src.few_shot_specialization import specialized_explanation st.set_page_config(page_title="VibeFinder", page_icon="๐ŸŽต", layout="centered") @st.cache_data def get_songs(): return load_songs("data/songs.csv") SONGS = get_songs() GENRES = sorted(set(s["genre"] for s in SONGS)) MOODS = sorted(set(s["mood"] for s in SONGS)) # โ”€โ”€ Sidebar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ st.sidebar.title("๐ŸŽต VibeFinder") st.sidebar.caption("AI-Enhanced Music Recommender") page = st.sidebar.radio("Navigate", ["Recommend", "Agent Workflow", "Reliability Tests", "Few-Shot Tones"]) # โ”€โ”€ Page: Recommend โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ if page == "Recommend": st.title("๐ŸŽต Find Your Vibe") st.write("Set your taste preferences and get personalized song picks.") col1, col2 = st.columns(2) with col1: genre = st.selectbox("Favorite Genre", GENRES, index=GENRES.index("lofi") if "lofi" in GENRES else 0) mood = st.selectbox("Current Mood", MOODS, index=MOODS.index("chill") if "chill" in MOODS else 0) with col2: energy = st.slider("Energy Level", 0.0, 1.0, 0.5, 0.05) likes_acoustic = st.checkbox("Prefer Acoustic Sound", value=False) mode = st.selectbox("Scoring Mode", ["default", "mood_first", "energy_focused", "popularity_aware"]) diversity = st.checkbox("Diversity Penalty (avoid repeat artists)", value=False) k = st.slider("Number of Results", 3, 10, 5) if st.button("๐Ÿ” Get Recommendations", type="primary"): prefs = {"genre": genre, "mood": mood, "energy": energy, "likes_acoustic": likes_acoustic} recs = recommend_songs(prefs, SONGS, k=k, mode=mode, diversity_penalty=diversity) st.subheader(f"Top {k} Songs for You") for i, (song, score, reasons) in enumerate(recs, 1): genre_match = "โœ…" if song["genre"] == genre else "โฌœ" mood_match = "โœ…" if song["mood"] == mood else "โฌœ" with st.expander(f"#{i} {song['title']} โ€” {song['artist']} | Score: {score:.2f} {genre_match}{mood_match}"): col_a, col_b, col_c = st.columns(3) col_a.metric("Genre", song["genre"]) col_b.metric("Mood", song["mood"]) col_c.metric("Energy", f"{song['energy']:.2f}") st.caption("Why recommended:") for r in reasons: st.write(f"โ€ข {r}") # โ”€โ”€ Page: Agent Workflow โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ elif page == "Agent Workflow": st.title("๐Ÿค– Agentic Workflow") st.write("Watch the AI plan, act, evaluate, and self-correct to find your best match.") col1, col2 = st.columns(2) with col1: genre = st.selectbox("Genre", GENRES) mood = st.selectbox("Mood", MOODS) with col2: energy = st.slider("Energy", 0.0, 1.0, 0.7, 0.05) if st.button("โ–ถ Run Agent", type="primary"): prefs = {"genre": genre, "mood": mood, "energy": energy} with st.spinner("Agent running Plan โ†’ Act โ†’ Evaluate โ†’ Refine..."): agent = RecommendationAgent(Recommender([])) recs, summary = agent.run(prefs, SONGS) quality = summary.get("final_quality_score", 0.0) iterations = summary.get("total_iterations", 0) # re-evaluate to get metrics _, evaluation = agent.evaluate(recs, prefs) metrics = evaluation.get("metrics", {}) # Quality gauge color = "green" if quality >= 0.65 else "orange" if quality >= 0.45 else "red" st.markdown(f"### Quality Score: :{color}[{quality:.2f}]") st.progress(float(quality)) st.caption(f"Completed in {iterations} iteration(s) ยท threshold = 0.65") if metrics: st.subheader("Evaluation Metrics") cols = st.columns(len(metrics)) for col, (k_m, v_m) in zip(cols, metrics.items()): col.metric(k_m.replace("_", " ").title(), f"{v_m:.2f}") st.subheader("Recommended Songs") for i, (song, score, reasons) in enumerate(recs, 1): with st.expander(f"#{i} {song['title']} โ€” {song['artist']} (score: {score:.2f})"): st.write(", ".join(reasons[:3])) # โ”€โ”€ Page: Reliability Tests โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ elif page == "Reliability Tests": st.title("โœ… Reliability Testing") st.write("Verify the AI gives consistent, robust, and fair results.") col1, col2 = st.columns(2) with col1: genre = st.selectbox("Test Genre", GENRES) mood = st.selectbox("Test Mood", MOODS) with col2: energy = st.slider("Energy", 0.0, 1.0, 0.5, 0.05) if st.button("๐Ÿงช Run Tests", type="primary"): prefs = {"genre": genre, "mood": mood, "energy": energy} tester = ReliabilityTester(SONGS) with st.spinner("Running reliability suite..."): report = tester.run_full_test_suite(prefs, SONGS) overall = report.get("overall_status", "UNKNOWN") passed = report.get("tests_passed", 0) total = report.get("tests_run", 0) if overall == "PASS": st.success(f"Overall: PASS โ€” {passed}/{total} tests passed") else: st.error(f"Overall: {overall} โ€” {passed}/{total} tests passed") # Individual test results for key in ["consistency", "robustness", "fairness", "explanation_alignment"]: result = report.get(key) if result is None: continue status = result.get("status", "SKIP") icon = "โœ…" if status == "PASS" else "โŒ" if status == "FAIL" else "โญ" with st.expander(f"{icon} {key.replace('_', ' ').title()} โ€” {status}"): for k_r, v_r in result.items(): if k_r not in ("test_name", "status"): st.write(f"**{k_r}:** {v_r}") # โ”€โ”€ Page: Few-Shot Tones โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ elif page == "Few-Shot Tones": st.title("๐ŸŽญ Few-Shot Specialization") st.write("Same song recommendation โ€” three specialized tones showing measurable output difference.") col1, col2 = st.columns(2) with col1: genre = st.selectbox("Genre", GENRES, index=GENRES.index("lofi") if "lofi" in GENRES else 0) mood = st.selectbox("Mood", MOODS, index=MOODS.index("chill") if "chill" in MOODS else 0) with col2: energy = st.slider("Energy", 0.0, 1.0, 0.4, 0.05) if st.button("๐ŸŽจ Generate Tones", type="primary"): prefs = {"genre": genre, "mood": mood, "energy": energy} recs = recommend_songs(prefs, SONGS, k=3) for i, (song, score, reasons) in enumerate(recs, 1): st.markdown(f"---\n**#{i} {song['title']}** by {song['artist']} โ€” Score: {score:.2f}") col_a, col_b, col_c = st.columns(3) baseline = specialized_explanation(song, score, reasons, "baseline") chill = specialized_explanation(song, score, reasons, "chill_student") hype = specialized_explanation(song, score, reasons, "hype_coach") with col_a: st.markdown("**๐Ÿ“‹ Baseline**") st.info(baseline) with col_b: st.markdown("**๐Ÿ˜Ž Chill Student**") st.success(chill) with col_c: st.markdown("**๐Ÿ’ช Hype Coach**") st.warning(hype)