# app/ui_streamlit.py import os, json from pathlib import Path import streamlit as st from app.main import get_env, ensure_index_exists from app.search import search # Streamlit config should be the first Streamlit call st.set_page_config(page_title="Grants Discovery RAG", layout="wide") # Environment + index _env = get_env() ensure_index_exists(_env) # ---------- helpers ---------- def _dedup_records(rows): seen, out = set(), [] for r in rows or []: k = r.get("id") or r.get("url") or r.get("title") if not k or k in seen: continue seen.add(k) out.append(r) return out # ---------- end helpers ---------- # ---------- optional diagnostics ---------- with st.expander("Diagnostics (optional)", expanded=False): idx = Path(_env["INDEX_DIR"]) st.write("INDEX_DIR:", str(idx)) st.write("faiss.index exists:", (idx / "faiss.index").exists()) st.write("meta.json exists:", (idx / "meta.json").exists()) if (idx / "meta.json").exists(): try: meta = json.loads((idx / "meta.json").read_text()) st.write("meta.json count:", len(meta)) st.write("meta head:", [{"id": m.get("id"), "title": m.get("title")} for m in meta[:2]]) except Exception as e: st.error(f"Failed to read meta.json: {e!r}") try: demo = search("transportation", _env, top_k=3, filters={}) st.write("sample search('transportation') results:", len(demo)) if demo: st.write(demo[:3]) except Exception as e: st.error(f"search() raised: {e!r}") # ---------- end diagnostics ---------- st.title("Grants Discovery RAG (Capacity Building)") preset = st.radio( "Quick topic:", ["General", "Elderly", "Prison Ministry", "Evangelism", "Vehicles/Transport", "FTA 5310"], horizontal=True ) default_q = { "General": "capacity building", "Elderly": "capacity building for seniors and aging services", "Prison Ministry": "capacity building for reentry and prison ministry", "Evangelism": "capacity building for faith and community outreach", "Vehicles/Transport": "capacity building transportation vehicles vans buses mobility", "FTA 5310": "5310 Enhanced Mobility Seniors Individuals with Disabilities", }.get(preset, "capacity building") # --- controls --- q = st.text_input("Search query", value=default_q) # No defaults -> no filtering unless the user selects something geo = st.multiselect("Geo filter (optional)", options=["US", "MD", "MA"], default=[]) categories = st.multiselect( "Category filter (optional)", options=["capacity_building", "elderly", "prison_ministry", "evangelism", "transportation", "vehicle"], default=[] ) top_k = st.slider("Results", 5, 50, 15) # Build filters only when selected filters = {} if geo: filters["geo"] = geo if categories: filters["categories"] = categories # <- use 'categories' key (not 'cats') col1, col2 = st.columns([1, 1]) with col1: if st.button("Search"): try: results = search(q, _env, top_k=top_k, filters=filters) results = _dedup_records(results) st.session_state["results"] = results except Exception as e: st.error(str(e)) with col2: if st.button("Export Results to CSV"): results = st.session_state.get("results", []) if not results: st.warning("No results to export. Run a search first.") else: os.makedirs(_env["EXPORT_DIR"], exist_ok=True) out_path = os.path.join(_env["EXPORT_DIR"], "results.csv") import pandas as pd pd.DataFrame(results).to_csv(out_path, index=False) st.success(f"Exported to {out_path}") st.markdown("---") results = st.session_state.get("results", []) if results: st.caption(f"Results: {len(results)}") for r in results: title = r.get("title", "(no title)") url = r.get("url", "") cats = r.get("categories") or r.get("cats") or [] geo_tags = r.get("geo") or [] st.markdown(f"### {title}") st.write(f"**Source:** {r.get('source','')} | **Geo:** {', '.join(geo_tags) if isinstance(geo_tags, list) else geo_tags} | **Categories:** {', '.join(cats) if isinstance(cats, list) else cats}") if url and not url.startswith("http"): st.caption("Note: This item may display an ID or number instead of a full link. Open on Grants.gov if needed.") st.write(f"[Open Link]({url}) \nScore: {r.get('score', 0):.3f}") st.markdown("---") else: st.info("Enter a query and click Search.")