IAMTFRMZA commited on
Commit
373b6fa
Β·
verified Β·
1 Parent(s): 58b1bba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -118
app.py CHANGED
@@ -1,119 +1,117 @@
1
  import streamlit as st
2
- from openai import OpenAI
3
- import time
4
- import os
5
- import uuid
6
- import firebase_admin
7
- from firebase_admin import credentials, firestore
8
-
9
- # πŸ” Firebase setup
10
- if not firebase_admin._apps:
11
- cred = credentials.Certificate("firebase-service-account.json")
12
- firebase_admin.initialize_app(cred)
13
-
14
- db = firestore.client()
15
-
16
- # πŸ” OpenAI setup
17
- openai_key = os.getenv("openai_key")
18
- assistant_id = os.getenv("ASSISTANT_ID")
19
- client = OpenAI(api_key=openai_key)
20
-
21
- # 🌐 Streamlit Config
22
- st.set_page_config(page_title="LOR Technologies AI Assistant", layout="wide")
23
-
24
- # 🎯 Session + User ID
25
- if "user_id" not in st.session_state:
26
- st.session_state["user_id"] = str(uuid.uuid4())
27
- user_id = st.session_state["user_id"]
28
-
29
- # πŸ–ΌοΈ LOR Branding + Styling
30
- st.markdown("""
31
- <style>
32
- .block-container {padding-top: 1rem; padding-bottom: 0rem;}
33
- header {visibility: hidden;}
34
- .stChatMessage { max-width: 85%; border-radius: 12px; padding: 8px; margin-bottom: 10px; }
35
- .stChatMessage[data-testid="stChatMessage-user"] { background: #f0f0f0; color: #000000; }
36
- .stChatMessage[data-testid="stChatMessage-assistant"] { background: #e3f2fd; color: #000000; }
37
- .lor-logo { vertical-align: middle; }
38
- </style>
39
- """, unsafe_allow_html=True)
40
-
41
- st.markdown("""
42
- <div style='text-align: center; margin-top: 20px; margin-bottom: -10px;'>
43
- <span style='display: inline-flex; align-items: center; gap: 8px;'>
44
- <img src='https://lortechnologies.com/wp-content/uploads/2023/03/LOR-Online-Logo.svg' width='100' class='lor-logo'/>
45
- <span style='font-size: 12px; color: gray;'>Powered by LOR Technologies</span>
46
- </span>
47
- </div>
48
- """, unsafe_allow_html=True)
49
-
50
- # πŸ” Get or create a thread ID
51
- def get_or_create_thread_id():
52
- doc_ref = db.collection("users").document(user_id)
53
- doc = doc_ref.get()
54
- if doc.exists:
55
- return doc.to_dict()["thread_id"]
56
- else:
57
- thread = client.beta.threads.create()
58
- doc_ref.set({"thread_id": thread.id, "created_at": firestore.SERVER_TIMESTAMP})
59
- return thread.id
60
-
61
- # πŸ’Ύ Save a message
62
- def save_message(role, content):
63
- db.collection("users").document(user_id).collection("messages").add({
64
- "role": role,
65
- "content": content,
66
- "timestamp": firestore.SERVER_TIMESTAMP
67
- })
68
-
69
- # πŸ’¬ Display chat history
70
- def display_chat_history():
71
- messages = db.collection("users").document(user_id).collection("messages").order_by("timestamp").stream()
72
- assistant_icon_html = "<img src='https://lortechnologies.com/wp-content/uploads/2023/03/LOR-Online-Logo.svg' width='20' style='vertical-align:middle;'/>"
73
- for msg in list(messages)[::-1]:
74
- data = msg.to_dict()
75
- if data["role"] == "user":
76
- st.markdown(f"<div class='stChatMessage' data-testid='stChatMessage-user'>πŸ‘€ <strong>You:</strong> {data['content']}</div>", unsafe_allow_html=True)
77
- else:
78
- st.markdown(f"<div class='stChatMessage' data-testid='stChatMessage-assistant'>{assistant_icon_html} <strong>LOR Assistant:</strong> {data['content']}</div>", unsafe_allow_html=True)
79
-
80
- # πŸš€ Main Chat UI
81
- input_col, clear_col = st.columns([9, 1])
82
- with input_col:
83
- user_input = st.chat_input("Type your message here...")
84
-
85
- with clear_col:
86
- if st.button("πŸ—‘οΈ", key="clear-chat", help="Clear Chat"):
87
- try:
88
- user_doc_ref = db.collection("users").document(user_id)
89
- for msg in user_doc_ref.collection("messages").stream():
90
- msg.reference.delete()
91
- user_doc_ref.delete()
92
- st.session_state.clear()
93
- st.rerun()
94
- except Exception as e:
95
- st.error(f"Failed to clear chat: {e}")
96
-
97
- thread_id = get_or_create_thread_id()
98
- display_chat_history()
99
-
100
- if user_input:
101
- # Send user message to OpenAI thread
102
- client.beta.threads.messages.create(thread_id=thread_id, role="user", content=user_input)
103
- save_message("user", user_input)
104
-
105
- with st.spinner("Thinking and typing... πŸ’­"):
106
- run = client.beta.threads.runs.create(thread_id=thread_id, assistant_id=assistant_id)
107
- while True:
108
- run_status = client.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run.id)
109
- if run_status.status == "completed":
110
- break
111
- time.sleep(1)
112
-
113
- messages_response = client.beta.threads.messages.list(thread_id=thread_id)
114
- latest_response = sorted(messages_response.data, key=lambda x: x.created_at)[-1]
115
- assistant_message = latest_response.content[0].text.value
116
-
117
- save_message("assistant", assistant_message)
118
- time.sleep(0.5)
119
- st.rerun()
 
1
  import streamlit as st
2
+ import requests
3
+
4
+ # ----------------- Streamlit Setup ----------------- #
5
+ st.set_page_config(page_title="Lease Agreement Extractor (OCR + AI)", layout="centered")
6
+ st.title("πŸ“„ Lease Agreement Extractor (OCR + AI)")
7
+
8
+ # ----------------- Assistant & API Config ----------------- #
9
+ OPENAI_ASSISTANT_ID = "asst_xBnNfiyWmVa4iF3CgXwJnmBt" # Replace with your assistant ID
10
+ OPENAI_API_KEY = "YOUR_OPENAI_API_KEY" # Replace with your OpenAI key
11
+
12
+ HEADERS = {
13
+ "Authorization": f"Bearer {OPENAI_API_KEY}",
14
+ "Content-Type": "application/json"
15
+ }
16
+
17
+ # ----------------- Upload Interface ----------------- #
18
+ uploaded_file = st.file_uploader("Upload a lease agreement (PDF, PNG, JPG)", type=["pdf", "png", "jpg", "jpeg"])
19
+
20
+ def upload_file_to_openai(file_bytes, filename):
21
+ files = {
22
+ "file": (filename, file_bytes, "application/octet-stream")
23
+ }
24
+ response = requests.post("https://api.openai.com/v1/files", headers={"Authorization": f"Bearer {OPENAI_API_KEY}"}, files=files)
25
+ return response.json().get("id")
26
+
27
+ def create_thread_and_run(file_id):
28
+ # Create thread
29
+ thread_res = requests.post("https://api.openai.com/v1/threads", headers=HEADERS, json={})
30
+ thread_data = thread_res.json()
31
+ thread_id = thread_data.get("id")
32
+
33
+ # Run assistant with structured lease extraction prompt
34
+ run_payload = {
35
+ "assistant_id": OPENAI_ASSISTANT_ID,
36
+ "instructions": """
37
+ You are an AI assistant designed to extract structured lease data from any uploaded agreement, even if it is scanned. Your responsibilities are:
38
+
39
+ - Accept PDF or image-based lease documents.
40
+ - Use OCR (Optical Character Recognition) if the document is non-selectable or scanned.
41
+ - Parse and extract key structured data fields from lease agreements.
42
+ - Output results in JSON format following a fixed schema.
43
+
44
+ OCR & Extraction Rules:
45
+ 1. If text cannot be extracted normally, perform OCR using image-to-text on each page.
46
+ 2. Preserve paragraph structure, detect headers, signature blocks, tables, and dates.
47
+
48
+ Expected JSON Output:
49
+ {
50
+ "document_title": "Lease Agreement Title or File Name",
51
+ "parties": {
52
+ "lessor": "Name of Lessor",
53
+ "lessee": "Name of Lessee"
54
+ },
55
+ "property_description": "Detailed description of leased property",
56
+ "term": {
57
+ "start_date": "YYYY-MM-DD",
58
+ "end_date": "YYYY-MM-DD",
59
+ "renewal_options": "Yes/No or Clause Text"
60
+ },
61
+ "financials": {
62
+ "monthly_rent": "Amount",
63
+ "deposit": "Amount",
64
+ "escalation_clause": "Text if present"
65
+ },
66
+ "obligations": {
67
+ "maintenance": "Lessor/Lessee",
68
+ "insurance": "Lessor/Lessee",
69
+ "subletting": "Allowed/Not allowed"
70
+ },
71
+ "signatures": [
72
+ {
73
+ "party": "Party Name",
74
+ "signed_by": "Full Name",
75
+ "date": "YYYY-MM-DD",
76
+ "position": "Title if listed"
77
+ }
78
+ ],
79
+ "raw_text_per_page": {
80
+ "page_1": "Full OCR text of page 1",
81
+ "page_2": "Full OCR text of page 2"
82
+ }
83
+ }
84
+
85
+ πŸ“Œ Behaviors:
86
+ - Automatically detect if OCR is needed.
87
+ - Normalize dates to YYYY-MM-DD.
88
+ - Use "Not Found" or null where info is missing.
89
+ - Return JSON only.
90
+ - If OCR text is low-confidence, include: "low_confidence": true
91
+ """,
92
+ "file_ids": [file_id]
93
+ }
94
+
95
+ run_res = requests.post(f"https://api.openai.com/v1/threads/{thread_id}/runs", headers=HEADERS, json=run_payload)
96
+ run_data = run_res.json()
97
+ run_id = run_data.get("id")
98
+
99
+ return thread_id, run_id
100
+
101
+ # ----------------- Main UI Flow ----------------- #
102
+ if uploaded_file is not None:
103
+ st.success("πŸ“„ File uploaded successfully.")
104
+
105
+ if st.button("πŸš€ Run Lease Extraction"):
106
+ with st.spinner("Uploading and invoking assistant..."):
107
+ file_id = upload_file_to_openai(uploaded_file.getvalue(), uploaded_file.name)
108
+ if file_id:
109
+ thread_id, run_id = create_thread_and_run(file_id)
110
+ st.success("βœ… Assistant run started!")
111
+ st.code(f"Thread ID: {thread_id}", language="text")
112
+ st.code(f"Run ID: {run_id}", language="text")
113
+ st.markdown("πŸ“Œ You can retrieve results via OpenAI API using these IDs.")
114
+ else:
115
+ st.error("❌ File upload to OpenAI failed.")
116
+ else:
117
+ st.info("Please upload a lease agreement file to begin.")