IAMTFRMZA commited on
Commit
48e8a8b
Β·
verified Β·
1 Parent(s): c7d0024

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -194
app.py CHANGED
@@ -14,203 +14,112 @@ st.set_page_config(page_title="Schlaeger Forrestdale TechDocAIA", layout="wide",
14
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
15
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧐")
16
 
17
- # ------------------ Load API Key ------------------
18
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
19
- if not OPENAI_API_KEY:
20
- st.error("❌ Missing OPENAI_API_KEY. Please set it in Hugging Face Space secrets.")
 
 
21
  st.stop()
22
 
23
  client = OpenAI(api_key=OPENAI_API_KEY)
24
 
25
- # ------------------ Tabs Setup ------------------
26
- tabs = st.tabs(["πŸ“‘ Contract", "πŸ“ Technical"])
27
-
28
- # ------------------ Contract Tab ------------------
29
- with tabs[0]:
30
- from urllib.parse import quote
31
-
32
- ASSISTANT_ID_CONTRACT = "asst_KsQRedoJUnEeStzfox1o06lO"
33
-
34
- if "contract_messages" not in st.session_state:
35
- st.session_state.contract_messages = []
36
- if "contract_thread_id" not in st.session_state:
37
- st.session_state.contract_thread_id = None
38
- if "contract_image_url" not in st.session_state:
39
- st.session_state.contract_image_url = None
40
- if "contract_image_updated" not in st.session_state:
41
- st.session_state.contract_image_updated = False
42
- if "contract_pending_prompt" not in st.session_state:
43
- st.session_state.contract_pending_prompt = None
44
-
45
- col1, col2 = st.columns([2, 1])
46
- with col1:
47
- st.markdown("### 🧠 Ask a Document-Specific Question")
48
- user_prompt = st.chat_input("Example: What is the defects liability period?")
49
-
50
- if user_prompt:
51
- st.session_state.contract_messages.append({"role": "user", "content": user_prompt})
52
- elif st.session_state.contract_pending_prompt:
53
- st.session_state.contract_messages.append({"role": "user", "content": st.session_state.contract_pending_prompt})
54
- st.session_state.contract_pending_prompt = None
55
-
56
- if st.session_state.contract_messages and st.session_state.contract_messages[-1]["role"] == "user":
57
- try:
58
- if st.session_state.contract_thread_id is None:
59
- thread = client.beta.threads.create()
60
- st.session_state.contract_thread_id = thread.id
61
-
62
- client.beta.threads.messages.create(
63
- thread_id=st.session_state.contract_thread_id,
64
- role="user",
65
- content=st.session_state.contract_messages[-1]["content"]
66
- )
67
-
68
- run = client.beta.threads.runs.create(
69
- thread_id=st.session_state.contract_thread_id,
70
- assistant_id=ASSISTANT_ID_CONTRACT
 
 
 
 
71
  )
72
-
73
- with st.spinner("πŸ€– Parsing and responding with referenced content..."):
74
- while True:
75
- run_status = client.beta.threads.runs.retrieve(
76
- thread_id=st.session_state.contract_thread_id,
77
- run_id=run.id
78
- )
79
- if run_status.status in ("completed", "failed", "cancelled"):
80
- break
81
- time.sleep(1)
82
-
83
- if run_status.status == "completed":
84
- messages = client.beta.threads.messages.list(thread_id=st.session_state.contract_thread_id)
85
- for message in reversed(messages.data):
86
- if message.role == "assistant":
87
- assistant_reply = message.content[0].text.value
88
- st.session_state.contract_messages.append({"role": "assistant", "content": assistant_reply})
89
-
90
- match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', assistant_reply)
91
- if match:
92
- doc_name = match.group(1).strip()
93
- page = int(match.group(2))
94
- page_str = f"{page:04d}"
95
- folder = quote(doc_name)
96
- image_url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{folder}/{folder}_page_{page_str}.png"
97
- st.session_state.contract_image_url = image_url
98
- st.session_state.contract_image_updated = True
99
- break
100
- else:
101
- st.error(f"⚠️ Assistant failed: {run_status.status}")
102
- st.rerun()
103
-
104
- except Exception as e:
105
- st.error(f"❌ Error: {e}")
106
-
107
- for msg in st.session_state.contract_messages:
108
- with st.chat_message(msg["role"]):
109
- st.markdown(msg["content"], unsafe_allow_html=True)
110
-
111
- with col2:
112
- if st.session_state.contract_image_url:
113
- try:
114
- response = requests.get(st.session_state.contract_image_url)
115
- response.raise_for_status()
116
- img = Image.open(BytesIO(response.content))
117
- st.image(img, caption="πŸ“„ OCR Page Image", use_container_width=True)
118
- except Exception as e:
119
- st.error(f"❗ Failed to load image: {e}")
120
-
121
- # ------------------ Technical Tab ------------------
122
- with tabs[1]:
123
- ASSISTANT_ID_TECHNICAL = "asst_DjvuWBc7tCvMbAhY7n1em4BZ"
124
-
125
- if "tech_messages" not in st.session_state:
126
- st.session_state.tech_messages = []
127
- if "tech_thread_id" not in st.session_state:
128
- st.session_state.tech_thread_id = None
129
- if "tech_results" not in st.session_state:
130
- st.session_state.tech_results = []
131
- if "tech_lightbox_url" not in st.session_state:
132
- st.session_state.tech_lightbox_url = None
133
-
134
- user_prompt = st.chat_input("Ask about plans, drawings or components")
135
- if user_prompt:
136
- st.session_state.tech_messages.append({"role": "user", "content": user_prompt})
137
-
138
- if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
139
- try:
140
- if st.session_state.tech_thread_id is None:
141
- thread = client.beta.threads.create()
142
- st.session_state.tech_thread_id = thread.id
143
-
144
- client.beta.threads.messages.create(
145
- thread_id=st.session_state.tech_thread_id,
146
- role="user",
147
- content=st.session_state.tech_messages[-1]["content"]
148
- )
149
-
150
- run = client.beta.threads.runs.create(
151
- thread_id=st.session_state.tech_thread_id,
152
- assistant_id=ASSISTANT_ID_TECHNICAL
153
- )
154
-
155
- with st.spinner("πŸ€– Parsing and responding..."):
156
- while True:
157
- run_status = client.beta.threads.runs.retrieve(
158
- thread_id=st.session_state.tech_thread_id,
159
- run_id=run.id
160
- )
161
- if run_status.status in ("completed", "failed", "cancelled"):
162
- break
163
- time.sleep(1)
164
-
165
- if run_status.status == "completed":
166
- messages = client.beta.threads.messages.list(thread_id=st.session_state.tech_thread_id)
167
- for message in reversed(messages.data):
168
- if message.role == "assistant":
169
- assistant_reply = message.content[0].text.value
170
- st.session_state.tech_messages.append({"role": "assistant", "content": assistant_reply})
171
- try:
172
- json_data = json.loads(assistant_reply.strip("`json "))
173
- st.session_state.tech_results = json_data
174
- except:
175
- st.session_state.tech_results = []
176
- break
177
- else:
178
- st.error(f"⚠️ Assistant failed: {run_status.status}")
179
- st.rerun()
180
-
181
- except Exception as e:
182
- st.error(f"❌ Error: {e}")
183
-
184
- if st.session_state.tech_results:
185
- disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.tech_results))
186
- selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
187
- page_size = 8
188
- page_num = st.number_input("Page", min_value=1, step=1, value=1)
189
-
190
- filtered_results = [r for r in st.session_state.tech_results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
191
- paged = filtered_results[(page_num - 1) * page_size : page_num * page_size]
192
-
193
- st.markdown("---")
194
- st.subheader("πŸ“‚ Drawing Results")
195
- cols = st.columns(4)
196
-
197
- for i, item in enumerate(paged):
198
- with cols[i % 4]:
199
- st.markdown(f"**{item['drawing_number']}**")
200
- st.markdown(f"_Discipline: {item['discipline']}_")
201
- st.caption(item.get("summary", ""))
202
- for url in item.get("images", [])[:1]:
203
- if st.button("πŸ–ΌοΈ View Image", key=f"view_{i}"):
204
- st.session_state.tech_lightbox_url = url
205
-
206
- if st.session_state.tech_lightbox_url:
207
- col_a, col_b = st.columns([1, 2])
208
- with col_b:
209
- st.image(st.session_state.tech_lightbox_url, caption="πŸ” Enlarged Preview", use_container_width=True)
210
- if st.button("❌ Close Viewer"):
211
- st.session_state.tech_lightbox_url = None
212
- st.rerun()
213
- else:
214
- for msg in st.session_state.tech_messages:
215
- with st.chat_message(msg["role"]):
216
- st.markdown(msg["content"], unsafe_allow_html=True)
 
14
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
15
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧐")
16
 
17
+ # ------------------ Load API Key and Assistant ID ------------------
18
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
19
+ ASSISTANT_ID = os.environ.get("ASSISTANT_ID")
20
+
21
+ if not OPENAI_API_KEY or not ASSISTANT_ID:
22
+ st.error("❌ Missing secrets. Please set both OPENAI_API_KEY and ASSISTANT_ID in Hugging Face Space secrets.")
23
  st.stop()
24
 
25
  client = OpenAI(api_key=OPENAI_API_KEY)
26
 
27
+ # ------------------ Session State Initialization ------------------
28
+ if "messages" not in st.session_state:
29
+ st.session_state.messages = []
30
+ if "thread_id" not in st.session_state:
31
+ st.session_state.thread_id = None
32
+ if "pending_prompt" not in st.session_state:
33
+ st.session_state.pending_prompt = None
34
+ if "results" not in st.session_state:
35
+ st.session_state.results = []
36
+ if "lightbox_url" not in st.session_state:
37
+ st.session_state.lightbox_url = None
38
+
39
+ # ------------------ Sidebar ------------------
40
+ st.sidebar.header("ℹ️ Information")
41
+ if st.sidebar.button("🧹 Clear Chat"):
42
+ st.session_state.messages = []
43
+ st.session_state.thread_id = None
44
+ st.session_state.pending_prompt = None
45
+ st.session_state.results = []
46
+ st.session_state.lightbox_url = None
47
+ st.rerun()
48
+
49
+ # ------------------ Chat Input ------------------
50
+ user_prompt = st.chat_input("Ask about plans, drawings or components")
51
+ if user_prompt:
52
+ st.session_state.messages.append({"role": "user", "content": user_prompt})
53
+
54
+ # ------------------ Assistant Query ------------------
55
+ if st.session_state.messages and st.session_state.messages[-1]["role"] == "user":
56
+ try:
57
+ if st.session_state.thread_id is None:
58
+ thread = client.beta.threads.create()
59
+ st.session_state.thread_id = thread.id
60
+
61
+ client.beta.threads.messages.create(
62
+ thread_id=st.session_state.thread_id,
63
+ role="user",
64
+ content=st.session_state.messages[-1]["content"]
65
+ )
66
+
67
+ run = client.beta.threads.runs.create(
68
+ thread_id=st.session_state.thread_id,
69
+ assistant_id=ASSISTANT_ID
70
+ )
71
+
72
+ with st.spinner("πŸ€– Parsing and responding..."):
73
+ while True:
74
+ run_status = client.beta.threads.runs.retrieve(
75
+ thread_id=st.session_state.thread_id,
76
+ run_id=run.id
77
  )
78
+ if run_status.status in ("completed", "failed", "cancelled"):
79
+ break
80
+ time.sleep(1)
81
+
82
+ if run_status.status != "completed":
83
+ st.error(f"⚠️ Assistant failed: {run_status.status}")
84
+ else:
85
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
86
+ for message in reversed(messages.data):
87
+ if message.role == "assistant":
88
+ assistant_reply = message.content[0].text.value
89
+ st.session_state.messages.append({"role": "assistant", "content": assistant_reply})
90
+ try:
91
+ json_data = json.loads(assistant_reply.strip("`json "))
92
+ st.session_state.results = json_data
93
+ except:
94
+ st.session_state.results = []
95
+ break
96
+ st.rerun()
97
+ except Exception as e:
98
+ st.error(f"❌ Error: {e}")
99
+
100
+ # ------------------ Sorting and Filters ------------------
101
+ if st.session_state.results:
102
+ disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.results))
103
+ selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
104
+ page_size = 8
105
+ page_num = st.number_input("Page", min_value=1, step=1, value=1)
106
+
107
+ filtered_results = [r for r in st.session_state.results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
108
+ paged = filtered_results[(page_num - 1) * page_size : page_num * page_size]
109
+
110
+ st.markdown("---")
111
+ st.subheader("πŸ“‚ Drawing Results")
112
+ cols = st.columns(4)
113
+
114
+ for i, item in enumerate(paged):
115
+ with cols[i % 4]:
116
+ st.markdown(f"**{item['drawing_number']}**")
117
+ st.markdown(f"_Discipline: {item['discipline']}_")
118
+ st.caption(item.get("summary", ""))
119
+ for url in item.get("images", [])[:1]:
120
+ st.image(url, use_column_width=True)
121
+
122
+ else:
123
+ for msg in st.session_state.messages:
124
+ with st.chat_message(msg["role"]):
125
+ st.markdown(msg["content"], unsafe_allow_html=True)