IAMTFRMZA commited on
Commit
42e0794
Β·
verified Β·
1 Parent(s): 7cb40ad

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -79
app.py CHANGED
@@ -10,12 +10,19 @@ from urllib.parse import quote
10
  from openai import OpenAI
11
 
12
  # ------------------ Authentication ------------------
 
 
 
 
 
 
 
13
  def login():
14
  st.title("πŸ” Login Required")
15
  email = st.text_input("Email")
16
  password = st.text_input("Password", type="password")
17
  if st.button("Login"):
18
- if email == "[email protected]" and password == "Pass.123":
19
  st.session_state.authenticated = True
20
  st.rerun()
21
  else:
@@ -33,100 +40,147 @@ st.set_page_config(page_title="Schlager Forrestdale DocAIAssist", layout="wide",
33
  st.title("πŸ“„ Schlager Forrestdale Document Assistant")
34
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧐")
35
 
36
- # ------------------ Tabs ------------------
37
- tab1, tab2 = st.tabs(["πŸ“‘ Contract", "πŸ“ Technical"])
38
-
39
- # ------------------ API Key ------------------
40
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
41
  if not OPENAI_API_KEY:
42
- st.error("❌ OPENAI_API_KEY missing in environment variables.")
43
  st.stop()
 
44
  client = OpenAI(api_key=OPENAI_API_KEY)
45
 
 
 
 
46
  # ------------------ Contract Tab ------------------
47
  with tab1:
48
  ASSISTANT_ID = "asst_KsQRedoJUnEeStzfox1o06lO"
49
 
50
- if "contract_messages" not in st.session_state:
51
- st.session_state.contract_messages = []
52
- if "contract_thread_id" not in st.session_state:
53
- st.session_state.contract_thread_id = None
54
- if "contract_image_url" not in st.session_state:
55
- st.session_state.contract_image_url = None
56
-
57
- user_input = st.chat_input("Ask about contract clauses, terms, or obligations")
58
- if user_input:
59
- st.session_state.contract_messages.append({"role": "user", "content": user_input})
60
-
61
- if st.session_state.contract_messages and st.session_state.contract_messages[-1]["role"] == "user":
62
- try:
63
- if st.session_state.contract_thread_id is None:
64
- thread = client.beta.threads.create()
65
- st.session_state.contract_thread_id = thread.id
66
-
67
- client.beta.threads.messages.create(
68
- thread_id=st.session_state.contract_thread_id,
69
- role="user",
70
- content=st.session_state.contract_messages[-1]["content"]
71
- )
72
-
73
- run = client.beta.threads.runs.create(
74
- thread_id=st.session_state.contract_thread_id,
75
- assistant_id=ASSISTANT_ID
76
- )
77
-
78
- with st.spinner("πŸ€– Thinking..."):
79
- while True:
80
- run_status = client.beta.threads.runs.retrieve(
81
- thread_id=st.session_state.contract_thread_id,
82
- run_id=run.id
83
- )
84
- if run_status.status in ("completed", "failed", "cancelled"):
85
- break
86
- time.sleep(1)
87
 
88
- if run_status.status == "completed":
89
- messages = client.beta.threads.messages.list(thread_id=st.session_state.contract_thread_id)
90
- for msg in reversed(messages.data):
91
- if msg.role == "assistant":
92
- content = msg.content[0].text.value
93
- st.session_state.contract_messages.append({"role": "assistant", "content": content})
94
-
95
- match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', content)
96
- if match:
97
- doc_name = match.group(1).strip()
98
- page = int(match.group(2))
99
- page_str = f"{page:04d}"
100
- folder = quote(doc_name)
101
- img_url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{folder}/{folder}_page_{page_str}.png"
102
- st.session_state.contract_image_url = img_url
103
- break
104
- except Exception as e:
105
- st.error(f"Error: {e}")
106
 
107
- for m in st.session_state.contract_messages:
108
- with st.chat_message(m["role"]):
109
- st.markdown(m["content"], unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- if st.session_state.contract_image_url:
112
- st.image(st.session_state.contract_image_url, caption="πŸ“„ Referenced Contract Page", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  # ------------------ Technical Tab ------------------
115
  with tab2:
116
  ASSISTANT_ID = "asst_DjvuWBc7tCvMbAhY7n1em4BZ"
117
-
118
  if "tech_messages" not in st.session_state:
119
  st.session_state.tech_messages = []
120
  if "tech_thread_id" not in st.session_state:
121
  st.session_state.tech_thread_id = None
122
  if "tech_results" not in st.session_state:
123
  st.session_state.tech_results = []
124
-
125
  st.session_state.tech_lightbox = None
126
 
127
- tech_prompt = st.chat_input("Ask about plans, drawings or components")
128
- if tech_prompt:
129
- st.session_state.tech_messages.append({"role": "user", "content": tech_prompt})
130
 
131
  if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
132
  try:
@@ -162,8 +216,7 @@ with tab2:
162
  content = msg.content[0].text.value
163
  st.session_state.tech_messages.append({"role": "assistant", "content": content})
164
  try:
165
- json_data = json.loads(content.strip("`json "))
166
- st.session_state.tech_results = json_data
167
  except:
168
  st.session_state.tech_results = []
169
  break
@@ -172,16 +225,15 @@ with tab2:
172
 
173
  with st.expander("πŸ”§ Options (Filter + Pagination)", expanded=False):
174
  disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.tech_results))
175
- selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
176
  page_size = 8
177
- page_num = st.number_input("Page", min_value=1, step=1, value=1)
178
 
179
  if st.session_state.tech_results:
180
  st.subheader("πŸ“‚ Results")
181
- filtered = [r for r in st.session_state.tech_results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
182
- paged = filtered[(page_num - 1) * page_size : page_num * page_size]
183
  cols = st.columns(4)
184
-
185
  for i, item in enumerate(paged):
186
  with cols[i % 4]:
187
  st.markdown(f"**πŸ“ {item['drawing_number']} ({item['discipline']})**")
@@ -199,4 +251,4 @@ with tab2:
199
  else:
200
  for msg in st.session_state.tech_messages:
201
  with st.chat_message(msg["role"]):
202
- st.markdown(msg["content"], unsafe_allow_html=True)
 
10
  from openai import OpenAI
11
 
12
  # ------------------ Authentication ------------------
13
+ VALID_USERS = {
14
+ "[email protected]": "Pass.123",
15
+ "[email protected]": "Pass.123",
16
+ "[email protected]": "Pass.123",
17
+ "[email protected]": "Pass.123",
18
+ }
19
+
20
  def login():
21
  st.title("πŸ” Login Required")
22
  email = st.text_input("Email")
23
  password = st.text_input("Password", type="password")
24
  if st.button("Login"):
25
+ if VALID_USERS.get(email) == password:
26
  st.session_state.authenticated = True
27
  st.rerun()
28
  else:
 
40
  st.title("πŸ“„ Schlager Forrestdale Document Assistant")
41
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧐")
42
 
43
+ # ------------------ Load API Key ------------------
 
 
 
44
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
45
  if not OPENAI_API_KEY:
46
+ st.error("❌ Missing OPENAI_API_KEY environment variable.")
47
  st.stop()
48
+
49
  client = OpenAI(api_key=OPENAI_API_KEY)
50
 
51
+ # ------------------ Tabs ------------------
52
+ tab1, tab2 = st.tabs(["πŸ“‘ Contract", "πŸ“ Technical"])
53
+
54
  # ------------------ Contract Tab ------------------
55
  with tab1:
56
  ASSISTANT_ID = "asst_KsQRedoJUnEeStzfox1o06lO"
57
 
58
+ for key in ["messages", "thread_id", "image_url", "image_updated", "pending_prompt"]:
59
+ if key not in st.session_state:
60
+ st.session_state[key] = None if "url" in key else []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ with st.sidebar:
63
+ st.header("ℹ️ Contract Tools")
64
+ if st.button("🧹 Clear Chat"):
65
+ for k in ["messages", "thread_id", "image_url", "image_updated", "pending_prompt"]:
66
+ st.session_state[k] = None if "url" in k else []
67
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ show_image = st.toggle("πŸ“‘ Show Page Image", value=True)
70
+ keyword = st.text_input("Search by Keyword", placeholder="e.g. defects, WHS, delay")
71
+ if st.button("πŸ”Ž Search Keyword") and keyword:
72
+ st.session_state.pending_prompt = f"Find clauses or references related to: {keyword}"
73
+
74
+ section_options = [
75
+ "Select a section...",
76
+ "1. Formal Instrument of Contract",
77
+ "2. Offer and Acceptance",
78
+ "3. Key Personnel",
79
+ "4. Contract Pricing",
80
+ "5. Specifications",
81
+ "6. WHS Policies",
82
+ "7. Penalties and Delays",
83
+ "8. Dispute Resolution",
84
+ "9. Principal Obligations"
85
+ ]
86
+ section = st.selectbox("πŸ“„ Jump to Section", section_options)
87
+ if section != section_options[0]:
88
+ st.session_state.pending_prompt = f"Summarize or list key points from section: {section}"
89
+
90
+ actions = [
91
+ "Select an action...",
92
+ "List all contractual obligations",
93
+ "Summarize payment terms",
94
+ "List WHS responsibilities",
95
+ "Find delay-related penalties",
96
+ "Extract dispute resolution steps"
97
+ ]
98
+ action = st.selectbox("βš™οΈ Common Queries", actions)
99
+ if action != actions[0]:
100
+ st.session_state.pending_prompt = action
101
+
102
+ chat_col, image_col = st.columns([2, 1])
103
+ with chat_col:
104
+ st.markdown("### 🧠 Ask a Document-Specific Question")
105
+ user_input = st.chat_input("Example: What is the defects liability period?")
106
+ if user_input:
107
+ st.session_state.messages.append({"role": "user", "content": user_input})
108
+ elif st.session_state.pending_prompt:
109
+ st.session_state.messages.append({"role": "user", "content": st.session_state.pending_prompt})
110
+ st.session_state.pending_prompt = None
111
+
112
+ if st.session_state.messages and st.session_state.messages[-1]["role"] == "user":
113
+ try:
114
+ if st.session_state.thread_id is None:
115
+ thread = client.beta.threads.create()
116
+ st.session_state.thread_id = thread.id
117
+
118
+ client.beta.threads.messages.create(
119
+ thread_id=st.session_state.thread_id,
120
+ role="user",
121
+ content=st.session_state.messages[-1]["content"]
122
+ )
123
+
124
+ run = client.beta.threads.runs.create(
125
+ thread_id=st.session_state.thread_id,
126
+ assistant_id=ASSISTANT_ID
127
+ )
128
+
129
+ with st.spinner("πŸ€– Thinking..."):
130
+ while True:
131
+ status = client.beta.threads.runs.retrieve(thread_id=st.session_state.thread_id, run_id=run.id)
132
+ if status.status in ("completed", "failed", "cancelled"):
133
+ break
134
+ time.sleep(1)
135
+
136
+ if status.status == "completed":
137
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
138
+ for m in reversed(messages.data):
139
+ if m.role == "assistant":
140
+ reply = m.content[0].text.value
141
+ st.session_state.messages.append({"role": "assistant", "content": reply})
142
+ match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', reply)
143
+ if match:
144
+ doc, page = match.group(1).strip(), int(match.group(2))
145
+ folder = quote(doc)
146
+ img_url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{folder}/{folder}_page_{page:04d}.png"
147
+ st.session_state.image_url = img_url
148
+ st.session_state.image_updated = True
149
+ break
150
+ else:
151
+ st.error("❌ Assistant failed.")
152
+ st.rerun()
153
+ except Exception as e:
154
+ st.error(f"❌ Error: {e}")
155
 
156
+ for msg in st.session_state.messages:
157
+ with st.chat_message(msg["role"]):
158
+ st.markdown(msg["content"], unsafe_allow_html=True)
159
+
160
+ with image_col:
161
+ if show_image and st.session_state.image_url:
162
+ try:
163
+ r = requests.get(st.session_state.image_url)
164
+ r.raise_for_status()
165
+ img = Image.open(BytesIO(r.content))
166
+ st.image(img, caption="πŸ“„ OCR Page Image", use_container_width=True)
167
+ except Exception as e:
168
+ st.error(f"πŸ–ΌοΈ Image failed: {e}")
169
 
170
  # ------------------ Technical Tab ------------------
171
  with tab2:
172
  ASSISTANT_ID = "asst_DjvuWBc7tCvMbAhY7n1em4BZ"
 
173
  if "tech_messages" not in st.session_state:
174
  st.session_state.tech_messages = []
175
  if "tech_thread_id" not in st.session_state:
176
  st.session_state.tech_thread_id = None
177
  if "tech_results" not in st.session_state:
178
  st.session_state.tech_results = []
 
179
  st.session_state.tech_lightbox = None
180
 
181
+ tech_input = st.chat_input("Ask about plans, drawings or components")
182
+ if tech_input:
183
+ st.session_state.tech_messages.append({"role": "user", "content": tech_input})
184
 
185
  if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
186
  try:
 
216
  content = msg.content[0].text.value
217
  st.session_state.tech_messages.append({"role": "assistant", "content": content})
218
  try:
219
+ st.session_state.tech_results = json.loads(content.strip("`json "))
 
220
  except:
221
  st.session_state.tech_results = []
222
  break
 
225
 
226
  with st.expander("πŸ”§ Options (Filter + Pagination)", expanded=False):
227
  disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.tech_results))
228
+ selected = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
229
  page_size = 8
230
+ page = st.number_input("Page", min_value=1, step=1, value=1)
231
 
232
  if st.session_state.tech_results:
233
  st.subheader("πŸ“‚ Results")
234
+ results = [r for r in st.session_state.tech_results if selected == "All" or r.get("discipline") == selected]
235
+ paged = results[(page - 1) * page_size : page * page_size]
236
  cols = st.columns(4)
 
237
  for i, item in enumerate(paged):
238
  with cols[i % 4]:
239
  st.markdown(f"**πŸ“ {item['drawing_number']} ({item['discipline']})**")
 
251
  else:
252
  for msg in st.session_state.tech_messages:
253
  with st.chat_message(msg["role"]):
254
+ st.markdown(msg["content"], unsafe_allow_html=True)