Spaces:
No application file
No application file
| """ | |
| Streamlit UI for an AI Patent Summarizer. | |
| Features: | |
| - Paste text or upload PDF/TXT (e.g., patent, application, office action). | |
| - Summarize the patent (configurable style & length). | |
| - Extract key points / claims & potential action items. | |
| Gemini client + model calls live in services/*.py | |
| so this file focuses only on user interface & app flow. | |
| """ | |
| import streamlit as st | |
| from services.document_loader import extract_text_from_pdf | |
| from services.patent_summarizer import summarize_text, extract_key_points_and_actions | |
| # ===================================================== | |
| # 1. STREAMLIT APP: PAGE CONFIG & TITLE | |
| # ===================================================== | |
| st.set_page_config( | |
| page_title="AI Patent Summarizer", | |
| page_icon="π", | |
| layout="wide", | |
| ) | |
| st.title("π AI Patent Summarizer") | |
| st.caption( | |
| "Upload a patent / patent-like document or paste text, then generate structured patent summaries " | |
| "or claims/key points using Google Gemini." | |
| ) | |
| # ===================================================== | |
| # 2. SESSION STATE INITIALIZATION | |
| # ===================================================== | |
| # We keep the current document text in session state | |
| # so it's shared across tabs and interactions. | |
| if "doc_text" not in st.session_state: | |
| st.session_state.doc_text = "" | |
| # ===================================================== | |
| # 3. SIDEBAR: MODES & SETTINGS | |
| # ===================================================== | |
| st.sidebar.header("βοΈ Patent Assistant Settings") | |
| # Two modes: patent summary + claims / key points | |
| mode = st.sidebar.radio( | |
| "What do you want to do?", | |
| ["Patent summary", "Claims & key points"], | |
| index=0, | |
| ) | |
| # Summary configuration (used when in "Patent summary" mode) | |
| summary_style = st.sidebar.selectbox( | |
| "Summary style", | |
| [ | |
| "High-level invention overview (non-technical)", | |
| "Technical deep-dive (for engineers/R&D)", | |
| "Legal-style summary (claims-focused)", | |
| "Executive brief for business stakeholders", | |
| ], | |
| index=0, | |
| ) | |
| summary_length = st.sidebar.selectbox( | |
| "Summary length", | |
| ["Very short", "Short", "Medium", "Detailed"], | |
| index=2, | |
| ) | |
| st.sidebar.markdown("---") | |
| st.sidebar.markdown("**Document type:** Patent / patent-like") | |
| st.sidebar.markdown("**Model:** gemini-2.5-flash") | |
| # ===================================================== | |
| # 4. DOCUMENT INPUT: PASTE OR UPLOAD | |
| # ===================================================== | |
| st.subheader("1οΈβ£ Upload or paste your patent document") | |
| tab1, tab2 = st.tabs(["βοΈ Paste text", "π Upload file"]) | |
| # ---------- Tab 1: Paste text manually ---------- | |
| with tab1: | |
| pasted_text = st.text_area( | |
| "Paste your patent text here (claims, description, office action, etc.):", | |
| height=220, | |
| placeholder="Paste text from a patent, application, office action, prior art, etc.", | |
| value=st.session_state.doc_text, | |
| ) | |
| # Button to set the pasted text as the active document | |
| if st.button("Use pasted text"): | |
| st.session_state.doc_text = pasted_text | |
| st.success("Patent document updated from pasted text.") | |
| # ---------- Tab 2: Upload file (PDF or TXT) ---------- | |
| with tab2: | |
| uploaded_file = st.file_uploader( | |
| "Upload a PDF or TXT file (e.g., patent, application, office action)", | |
| type=["pdf", "txt"], | |
| ) | |
| if uploaded_file is not None: | |
| # Extract text depending on file type | |
| if uploaded_file.type == "application/pdf": | |
| file_bytes = uploaded_file.read() | |
| doc_text = extract_text_from_pdf(file_bytes) | |
| else: # text/plain | |
| doc_text = uploaded_file.read().decode("utf-8", errors="ignore") | |
| # Show extracted text so the user can review/edit it | |
| st.text_area( | |
| "Extracted text (you can edit before applying):", | |
| value=doc_text, | |
| height=220, | |
| key="uploaded_text_area", | |
| ) | |
| # Button to set uploaded text as active document | |
| if st.button("Use uploaded text"): | |
| st.session_state.doc_text = st.session_state.uploaded_text_area | |
| st.success(f"Patent document loaded from: {uploaded_file.name}") | |
| # ===================================================== | |
| # 5. DOCUMENT STATS | |
| # ===================================================== | |
| if st.session_state.doc_text: | |
| num_words = len(st.session_state.doc_text.split()) | |
| num_chars = len(st.session_state.doc_text) | |
| st.markdown( | |
| f"π **Current document length:** {num_words} words " | |
| f"({num_chars} characters)." | |
| ) | |
| else: | |
| st.info( | |
| "No patent document loaded yet. Paste text or upload a file to get started." | |
| ) | |
| # ===================================================== | |
| # 6. MODE A: PATENT SUMMARY | |
| # ===================================================== | |
| if mode == "Patent summary": | |
| st.subheader("2οΈβ£ Summarize your patent") | |
| st.markdown( | |
| "The assistant will aim to produce a structured patent-aware summary.\n\n" | |
| "- For *High-level overview*, it focuses on the core idea in simple language.\n" | |
| "- For *Technical deep-dive*, it emphasizes architecture, components, and method steps.\n" | |
| "- For *Legal-style summary*, it emphasizes claims, scope, and relationships.\n" | |
| ) | |
| if not st.session_state.doc_text: | |
| st.warning("Please load or paste a patent document first.") | |
| else: | |
| if st.button("Generate patent summary π"): | |
| with st.spinner("Summarizing patent with Gemini..."): | |
| try: | |
| # NOTE: | |
| # `summary_style` is now patent-specific (see options above). | |
| # You can adjust the prompt logic inside services/patent_summarizer.py | |
| # to interpret these styles and build a patent-aware prompt. | |
| summary = summarize_text( | |
| st.session_state.doc_text, | |
| style=summary_style, | |
| length=summary_length, | |
| ) | |
| st.subheader("π Patent Summary") | |
| st.write(summary) | |
| # Optional: let the user download the summary as text | |
| st.download_button( | |
| label="π₯ Download summary as TXT", | |
| data=summary, | |
| file_name="patent_summary.txt", | |
| mime="text/plain", | |
| ) | |
| except Exception as e: | |
| st.error(f"Error while summarizing patent: {e}") | |
| # ===================================================== | |
| # 7. MODE B: CLAIMS & KEY POINTS | |
| # ===================================================== | |
| elif mode == "Claims & key points": | |
| st.subheader("2οΈβ£ Extract claims, key points & potential actions") | |
| st.markdown( | |
| "This mode is useful for quickly seeing:\n" | |
| "- Main independent/dependent claims (as understood from text)\n" | |
| "- Key technical features\n" | |
| "- Potential follow-ups or action items (e.g., for review, responses, or product impact)\n" | |
| ) | |
| if not st.session_state.doc_text: | |
| st.warning("Please load or paste a patent document first.") | |
| else: | |
| if st.button("Extract claims & key points β "): | |
| with st.spinner("Extracting claims, key points, and action items..."): | |
| try: | |
| result = extract_key_points_and_actions( | |
| st.session_state.doc_text | |
| ) | |
| st.subheader("π Claims, Key Points & Action Items") | |
| st.write(result) | |
| # Optional: download as text file | |
| st.download_button( | |
| label="π₯ Download as TXT", | |
| data=result, | |
| file_name="patent_claims_key_points.txt", | |
| mime="text/plain", | |
| ) | |
| except Exception as e: | |
| st.error(f"Error while extracting from patent: {e}") | |