import base64 import html import uuid import streamlit as st from ingestion.qdrant_store import ensure_history_collection, ensure_jobs_collection, get_client from rag.pipeline import query st.set_page_config( page_title="JobRAG — Job Search Assistant", page_icon=":material/work:", layout="wide", initial_sidebar_state="expanded", ) CUSTOM_CSS = """ """ def _svg_b64(path: str, replacements: dict | None = None) -> str: with open(path, "rb") as f: data = f.read() for old, new in (replacements or {}).items(): data = data.replace(old.encode(), new.encode()) return base64.b64encode(data).decode() _SEND_B64 = _svg_b64("send-svgrepo-com.svg", {'stroke="#000000"': 'stroke="#ffffff"'}) _SIDEBAR_B64 = _svg_b64("side-list-svgrepo-com.svg") _ICON_CSS_RAW = """""" ICON_CSS = _ICON_CSS_RAW.replace("__SEND__", _SEND_B64).replace("__SIDEBAR__", _SIDEBAR_B64) SUGGESTIONS = [ "Which companies are actively hiring right now?", "What skills are most in demand?", "Are there any remote software engineer roles?", "What seniority levels are available?", "Which jobs require Python or Go?", "What are the most common job requirements?", ] @st.cache_resource(show_spinner="Connecting to knowledge base…") def _init_db(): client = get_client() ensure_jobs_collection(client) ensure_history_collection(client) return client def _render_sources(sources: list) -> None: if not sources: return st.markdown( f'
Sources · {len(sources)}
', unsafe_allow_html=True, ) for job in sources: title = html.escape(job.get("title") or "Untitled role") company = html.escape(job.get("company") or "Unknown company") score = html.escape(str(job.get("score", "—"))) location = html.escape(job.get("location") or "") seniority = html.escape(job.get("seniority_level") or "") employment = html.escape(job.get("employment_type") or "") description = job.get("description") or "No description available." desc_preview = html.escape(description[:400] + ("…" if len(description) > 400 else "")) tags_html = "" if location: tags_html += f'' if seniority: tags_html += f'' if employment: tags_html += f'' tags_html += f'{score}' st.markdown( f"""{title}
{company}
{desc_preview}
JobRAG
' 'Semantic search over live job listings.
', unsafe_allow_html=True, ) st.markdown('Dataset
', unsafe_allow_html=True) st.markdown( """Software Engineer · Berlin
Germany-focused listings refreshed on a scheduled ingestion pipeline.
Session
', unsafe_allow_html=True) st.markdown( f'Stack
', unsafe_allow_html=True) st.markdown( """Ask natural-language questions about Software Engineer openings in Berlin. Every answer is grounded in retrieved listings.
Try asking
', unsafe_allow_html=True) cols = st.columns(2) for i, suggestion in enumerate(SUGGESTIONS): with cols[i % 2]: if st.button(suggestion, use_container_width=True, key=f"suggest_{i}"): st.session_state._prefill = suggestion st.rerun() st.markdown(CUSTOM_CSS, unsafe_allow_html=True) st.markdown(ICON_CSS, unsafe_allow_html=True) try: _init_db() except Exception as e: st.error(f"Could not connect to the knowledge base: {e}") st.stop() if "session_id" not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) if "user_id" not in st.session_state: st.session_state.user_id = str(uuid.uuid4()) if "messages" not in st.session_state: st.session_state.messages = [] _render_sidebar() _render_hero() if not st.session_state.messages: _render_suggestions() for msg in st.session_state.messages: with st.chat_message(msg["role"]): st.markdown(msg["content"]) if msg["role"] == "assistant" and msg.get("sources"): _render_sources(msg["sources"]) prefill = st.session_state.pop("_prefill", None) with st.form("chat_form", clear_on_submit=True): col_text, col_btn = st.columns([15, 1]) with col_text: user_text = st.text_area( "", placeholder="Ask about roles, skills, companies, or requirements…", label_visibility="collapsed", height=68, ) with col_btn: submitted = st.form_submit_button("↑", use_container_width=False) user_input = prefill or (user_text.strip() if submitted and user_text.strip() else None) if user_input: st.session_state.messages.append({"role": "user", "content": user_input, "sources": None}) with st.chat_message("user"): st.markdown(user_input) with st.chat_message("assistant"): try: stream, sources = query( user_message=user_input, user_id=st.session_state.user_id, session_id=st.session_state.session_id, ) response_text = st.write_stream(stream) _render_sources(sources) except Exception as e: response_text = f"Something went wrong: {e}" sources = [] st.error(response_text) st.session_state.messages.append({ "role": "assistant", "content": response_text, "sources": sources, }) st.rerun()