IAMTFRMZA commited on
Commit
dd02bbc
Β·
verified Β·
1 Parent(s): 8c7ee14

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -100
app.py CHANGED
@@ -14,112 +14,170 @@ 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 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
  with st.expander("πŸ”§ Options (Filter + Pagination)", expanded=False):
103
- disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.results))
104
  selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
105
  page_size = 8
106
  page_num = st.number_input("Page", min_value=1, step=1, value=1)
107
 
108
- filtered_results = [r for r in st.session_state.results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
109
- paged = filtered_results[(page_num - 1) * page_size : page_num * page_size]
110
-
111
- st.markdown("---")
112
- st.subheader("πŸ“‚ Drawing Results")
113
- cols = st.columns(4)
114
-
115
- for i, item in enumerate(paged):
116
- with cols[i % 4]:
117
- st.markdown(f"**{item['drawing_number']}**")
118
- st.markdown(f"_Discipline: {item['discipline']}_")
119
- st.caption(item.get("summary", ""))
120
- for url in item.get("images", [])[:1]:
121
- st.image(url, caption=item['drawing_number'], use_container_width=True)
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)
 
 
 
 
 
 
 
14
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
15
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧐")
16
 
17
+ # ------------------ Tabs ------------------
18
+ tab1, tab2 = st.tabs(["πŸ“‘ Contract", "πŸ“ Technical"])
 
19
 
20
+ # ------------------ API Key ------------------
21
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
22
+ if not OPENAI_API_KEY:
23
+ st.error("❌ OPENAI_API_KEY missing in environment variables.")
24
  st.stop()
 
25
  client = OpenAI(api_key=OPENAI_API_KEY)
26
 
27
+ # ------------------ Contract Tab ------------------
28
+ with tab1:
29
+ ASSISTANT_ID = "asst_KsQRedoJUnEeStzfox1o06lO"
30
+
31
+ if "contract_messages" not in st.session_state:
32
+ st.session_state.contract_messages = []
33
+ if "contract_thread_id" not in st.session_state:
34
+ st.session_state.contract_thread_id = None
35
+ if "contract_image_url" not in st.session_state:
36
+ st.session_state.contract_image_url = None
37
+
38
+ user_input = st.chat_input("Ask about contract clauses, terms, or obligations")
39
+ if user_input:
40
+ st.session_state.contract_messages.append({"role": "user", "content": user_input})
41
+
42
+ if st.session_state.contract_messages and st.session_state.contract_messages[-1]["role"] == "user":
43
+ try:
44
+ if st.session_state.contract_thread_id is None:
45
+ thread = client.beta.threads.create()
46
+ st.session_state.contract_thread_id = thread.id
47
+
48
+ client.beta.threads.messages.create(
49
+ thread_id=st.session_state.contract_thread_id,
50
+ role="user",
51
+ content=st.session_state.contract_messages[-1]["content"]
52
+ )
53
+
54
+ run = client.beta.threads.runs.create(
55
+ thread_id=st.session_state.contract_thread_id,
56
+ assistant_id=ASSISTANT_ID
57
+ )
58
+
59
+ with st.spinner("πŸ€– Thinking..."):
60
+ while True:
61
+ run_status = client.beta.threads.runs.retrieve(
62
+ thread_id=st.session_state.contract_thread_id,
63
+ run_id=run.id
64
+ )
65
+ if run_status.status in ("completed", "failed", "cancelled"):
66
+ break
67
+ time.sleep(1)
68
+
69
+ if run_status.status == "completed":
70
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.contract_thread_id)
71
+ for msg in reversed(messages.data):
72
+ if msg.role == "assistant":
73
+ content = msg.content[0].text.value
74
+ st.session_state.contract_messages.append({"role": "assistant", "content": content})
75
+
76
+ match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', content)
77
+ if match:
78
+ doc_name = match.group(1).strip()
79
+ page = int(match.group(2))
80
+ page_str = f"{page:04d}"
81
+ folder = quote(doc_name)
82
+ img_url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{folder}/{folder}_page_{page_str}.png"
83
+ st.session_state.contract_image_url = img_url
84
+ break
85
+ except Exception as e:
86
+ st.error(f"Error: {e}")
87
+
88
+ for m in st.session_state.contract_messages:
89
+ with st.chat_message(m["role"]):
90
+ st.markdown(m["content"], unsafe_allow_html=True)
91
+
92
+ if st.session_state.contract_image_url:
93
+ st.image(st.session_state.contract_image_url, caption="πŸ“„ Referenced Contract Page", use_container_width=True)
94
+
95
+ # ------------------ Technical Tab ------------------
96
+ with tab2:
97
+ ASSISTANT_ID = "asst_DjvuWBc7tCvMbAhY7n1em4BZ"
98
+
99
+ if "tech_messages" not in st.session_state:
100
+ st.session_state.tech_messages = []
101
+ if "tech_thread_id" not in st.session_state:
102
+ st.session_state.tech_thread_id = None
103
+ if "tech_results" not in st.session_state:
104
+ st.session_state.tech_results = []
105
+
106
+ st.session_state.tech_lightbox = None
107
+
108
+ tech_prompt = st.chat_input("Ask about plans, drawings or components")
109
+ if tech_prompt:
110
+ st.session_state.tech_messages.append({"role": "user", "content": tech_prompt})
111
+
112
+ if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
113
+ try:
114
+ if st.session_state.tech_thread_id is None:
115
+ thread = client.beta.threads.create()
116
+ st.session_state.tech_thread_id = thread.id
117
+
118
+ client.beta.threads.messages.create(
119
+ thread_id=st.session_state.tech_thread_id,
120
+ role="user",
121
+ content=st.session_state.tech_messages[-1]["content"]
122
+ )
123
+
124
+ run = client.beta.threads.runs.create(
125
+ thread_id=st.session_state.tech_thread_id,
126
+ assistant_id=ASSISTANT_ID
127
+ )
128
+
129
+ with st.spinner("πŸ” Searching technical drawings..."):
130
+ while True:
131
+ run_status = client.beta.threads.runs.retrieve(
132
+ thread_id=st.session_state.tech_thread_id,
133
+ run_id=run.id
134
+ )
135
+ if run_status.status in ("completed", "failed", "cancelled"):
136
+ break
137
+ time.sleep(1)
138
+
139
+ if run_status.status == "completed":
140
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.tech_thread_id)
141
+ for msg in reversed(messages.data):
142
+ if msg.role == "assistant":
143
+ content = msg.content[0].text.value
144
+ st.session_state.tech_messages.append({"role": "assistant", "content": content})
145
+ try:
146
+ json_data = json.loads(content.strip("`json "))
147
+ st.session_state.tech_results = json_data
148
+ except:
149
+ st.session_state.tech_results = []
150
+ break
151
+ except Exception as e:
152
+ st.error(f"❌ Technical Assistant Error: {e}")
153
+
154
  with st.expander("πŸ”§ Options (Filter + Pagination)", expanded=False):
155
+ disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.tech_results))
156
  selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
157
  page_size = 8
158
  page_num = st.number_input("Page", min_value=1, step=1, value=1)
159
 
160
+ if st.session_state.tech_results:
161
+ st.subheader("πŸ“‚ Drawing Results")
162
+ filtered = [r for r in st.session_state.tech_results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
163
+ paged = filtered[(page_num - 1) * page_size : page_num * page_size]
164
+ cols = st.columns(4)
165
+
166
+ for i, item in enumerate(paged):
167
+ with cols[i % 4]:
168
+ st.markdown(f"**πŸ“ {item['drawing_number']} ({item['discipline']})**")
169
+ st.caption(item.get("summary", ""))
170
+ for url in item.get("images", [])[:1]:
171
+ if st.button("πŸ–ΌοΈ View Drawing Details", key=f"thumb_{i}"):
172
+ st.session_state.tech_lightbox = url
173
+ st.image(url, caption=f"{item['drawing_number']} - Page 1", use_container_width=True)
174
+
175
+ if st.session_state.tech_lightbox:
176
+ st.image(st.session_state.tech_lightbox, caption="πŸ” Enlarged Drawing Preview", use_container_width=True)
177
+ if st.button("❌ Close Preview"):
178
+ st.session_state.tech_lightbox = None
179
+ st.rerun()
180
+ else:
181
+ for msg in st.session_state.tech_messages:
182
+ with st.chat_message(msg["role"]):
183
+ st.markdown(msg["content"], unsafe_allow_html=True)