# ───────────────────────────────────────────────────────────── # Stage 1 UX Shell – FormPilot (no backend yet) # ───────────────────────────────────────────────────────────── import streamlit as st import pandas as pd from pathlib import Path from io import BytesIO import os from dotenv import load_dotenv load_dotenv() from qdrant_client import QdrantClient from langchain_community.vectorstores import Qdrant from langchain_openai import OpenAIEmbeddings from rag.qa_chain import get_answer from rag.ocr_azure import parse_passport_azure if "AZURE_DOC_KEY" not in os.environ: st.warning("⚠️ OCR disabled – set AZURE_DOC_KEY & AZURE_DOC_ENDPOINT") st.set_page_config( page_title="FormPilot – Immigration Paralegal Copilot", page_icon="🛡️", layout="wide", ) # Add custom CSS for better display of extracted information st.markdown(""" """, unsafe_allow_html=True) # ------------------------------------------------------------ # Helper: generate checklist table with extracted information # ------------------------------------------------------------ def build_checklist(profile=None): fields = [ ("I‑485 Part 1 – Full Name", "pending"), ("I‑485 Part 1 – A‑Number", "pending"), ("I‑485 Part 1 – Date of Birth", "pending"), ("I‑485 Part 2 – Basis of Application", "pending"), ] df = pd.DataFrame(fields, columns=["Field", "Status"]) # Update with extracted information if available if profile: # Full Name if profile.get('FirstName') and profile.get('LastName'): full_name = f"{profile.get('FirstName')} {profile.get('LastName')}" df.loc[df["Field"].str.contains("Full Name"), "Status"] = full_name # A-Number if profile.get('ANumber'): df.loc[df["Field"].str.contains("A‑Number"), "Status"] = profile.get('ANumber') # Date of Birth if profile.get('DateOfBirth'): df.loc[df["Field"].str.contains("Date of Birth"), "Status"] = profile.get('DateOfBirth') return df # ------------------------------------------------------------ # Helper: Format extracted information for display # ------------------------------------------------------------ def display_extracted_info(profile): if not profile: return st.info("No information extracted from documents.") st.markdown('
', unsafe_allow_html=True) # Display First Name st.markdown( f'
' f'First Name: ' f'' f'{profile.get("FirstName", "Not found")}' f'
', unsafe_allow_html=True ) # Display Last Name st.markdown( f'
' f'Last Name: ' f'' f'{profile.get("LastName", "Not found")}' f'
', unsafe_allow_html=True ) # Display Date of Birth st.markdown( f'
' f'Date of Birth: ' f'' f'{profile.get("DateOfBirth", "Not found")}' f'
', unsafe_allow_html=True ) # Display A-Number (if available) st.markdown( f'
' f'A-Number: ' f'' f'{profile.get("ANumber", "Not applicable")}' f'
', unsafe_allow_html=True ) st.markdown('
', unsafe_allow_html=True) # ------------------------------------------------------------ # Sidebar – navigation + brand # ------------------------------------------------------------ with st.sidebar: st.title("⚖️+📜+🛡️+🔍 FormPilot") if "stage" not in st.session_state: st.session_state.stage = "home" st.markdown("---") if st.button("🏠 Home"): st.session_state.stage = "home" if st.button("📄 Draft Packet"): if "uploaded_files" in st.session_state: st.session_state.stage = "draft" # ------------------------------------------------------------ # Stage A – Upload page # ------------------------------------------------------------ if st.session_state.stage == "home": st.header("New Case – Build I‑485 Package") uploaded_files = st.file_uploader( "Upload client documents (passport, visa, etc.)", type=["pdf", "jpg", "jpeg", "png"], accept_multiple_files=True, ) form_choice = st.selectbox( "Select USCIS Form to prepare", options=["I‑485 (Adjustment of Status)"], ) # Store uploads in session so we can view later if st.button("🚀 Build Package"): if not uploaded_files: st.warning("Please upload at least one document.") else: st.session_state.uploaded_files = uploaded_files st.session_state.form_choice = form_choice st.session_state.stage = "draft" st.rerun() st.info( """ **What happens next?** In Stage 2+ the AI will ingest your uploads, retrieve instructions, and draft the packet. For now, we jump to a placeholder Draft view. """ ) # ------------------------------------------------------------ # Stage B – Draft Packet page # ------------------------------------------------------------ elif st.session_state.stage == "draft": st.header("Draft Packet (Stage 3 – Retrieval MVP))") # Two-column layout: preview & checklist col_left, col_right = st.columns([2, 1]) with col_left: st.subheader("📄 PDF Preview") st.write( "A preview of the filled I‑485 will appear here once AI pre‑fill is ready." ) st.image( "https://placehold.co/600x800?text=PDF+Preview", caption="Static placeholder preview", ) with col_right: st.subheader("✅ Document Information") profile = {} # Try to get profile from session state first if "profile" in st.session_state: profile = st.session_state.profile # Otherwise, try to process the first uploaded file elif "uploaded_files" in st.session_state and st.session_state.uploaded_files: with st.spinner("Extracting information..."): data = st.session_state.uploaded_files[0].getvalue() try: profile = parse_passport_azure(data) st.session_state.profile = profile except Exception as e: st.error(f"Error extracting information: {str(e)}") # Display nicely formatted extracted information if profile and (profile.get("FirstName") or profile.get("LastName") or profile.get("DateOfBirth")): display_extracted_info(profile) # Show checklist with auto-filled fields st.subheader("Form Field Checklist") df = build_checklist(profile) st.dataframe(df, hide_index=True, width=350) # Show the raw JSON for debugging with st.expander("Raw Extracted Data"): st.json(profile) else: st.warning("No profile information extracted from documents.") df = build_checklist() st.dataframe(df, hide_index=True, width=350) st.markdown("---") st.subheader("Ask about I‑485 instructions") q = st.text_input("Your question", key="qa") if q: with st.spinner("Retrieving..."): ans, cites = get_answer(q) st.success(ans) st.caption("Sources: " + ", ".join(sorted(cites))) # ------------------------------------------------------------ # (Optional) Future stage for OCR parsing # ------------------------------------------------------------ # Example usage of doc_parse_stub if False: for file in st.session_state.get("uploaded_files", []): data = file.read() parsed = parse_passport(data) st.write(parsed)