IAMTFRMZA commited on
Commit
2e0eb4d
Β·
verified Β·
1 Parent(s): 56b7b50

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -116
app.py CHANGED
@@ -13,111 +13,183 @@ st.set_page_config(page_title="Schlaeger Forrestdale DocAIA", layout="wide", ini
13
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
14
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧠")
15
 
16
- # ------------------ Load API Key and Assistant ID ------------------
17
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
18
- ASSISTANT_ID = os.environ.get("ASSISTANT_ID")
 
19
 
20
- if not OPENAI_API_KEY or not ASSISTANT_ID:
21
- st.error("❌ Missing secrets. Please set both OPENAI_API_KEY and ASSISTANT_ID in Hugging Face Space secrets.")
22
  st.stop()
23
 
24
  client = OpenAI(api_key=OPENAI_API_KEY)
25
 
26
- # ------------------ Session State Initialization ------------------
27
- if "messages" not in st.session_state:
28
- st.session_state.messages = []
29
- if "thread_id" not in st.session_state:
30
- st.session_state.thread_id = None
31
- if "image_url" not in st.session_state:
32
- st.session_state.image_url = None
33
- if "image_updated" not in st.session_state:
34
- st.session_state.image_updated = False
35
- if "pending_prompt" not in st.session_state:
36
- st.session_state.pending_prompt = None
37
-
38
- # ------------------ Sidebar ------------------
39
- st.sidebar.header("ℹ️ Information")
40
- if st.sidebar.button("🧹 Clear Chat"):
41
- st.session_state.messages = []
42
- st.session_state.thread_id = None
43
- st.session_state.image_url = None
44
- st.session_state.image_updated = False
45
- st.session_state.pending_prompt = None
46
- st.rerun()
47
-
48
- show_image = st.sidebar.toggle("πŸ“‘ Show Page Image", value=True)
49
-
50
- st.sidebar.subheader("πŸ“˜ Document Tools")
51
- st.sidebar.markdown("Use the tools below to locate relevant clauses and actions:")
52
-
53
- keyword = st.sidebar.text_input("Search by Keyword", placeholder="e.g. defects, WHS, delay")
54
- if st.sidebar.button("πŸ”Ž Search Keyword") and keyword:
55
- st.session_state.pending_prompt = f"Find clauses or references related to: {keyword}"
56
-
57
- section_options = [
58
- "Select a section...",
59
- "1. Formal Instrument of Contract",
60
- "2. Offer and Acceptance",
61
- "3. Key Personnel",
62
- "4. Contract Pricing",
63
- "5. Specifications",
64
- "6. WHS Policies",
65
- "7. Penalties and Delays",
66
- "8. Dispute Resolution",
67
- "9. Principal Obligations"
68
- ]
69
- section_select = st.sidebar.selectbox("πŸ“„ Jump to Section", section_options)
70
- if section_select != section_options[0]:
71
- st.session_state.pending_prompt = f"Summarize or list key points from section: {section_select}"
72
-
73
- actions = [
74
- "Select an action...",
75
- "List all contractual obligations",
76
- "Summarize payment terms",
77
- "List WHS responsibilities",
78
- "Find delay-related penalties",
79
- "Extract dispute resolution steps"
80
- ]
81
- action_select = st.sidebar.selectbox("βš™οΈ Common Contract Queries", actions)
82
- if action_select != actions[0]:
83
- st.session_state.pending_prompt = action_select
84
-
85
- # ------------------ Layout: Chat + Image ------------------
86
- chat_col, image_col = st.columns([2, 1])
87
-
88
- # ------------------ Chat Interface ------------------
89
- with chat_col:
90
- st.markdown("### 🧠 Ask a Document-Specific Question")
91
- user_prompt = st.chat_input("Example: What is the defects liability period?")
92
-
93
- # Use pending prompt from sidebar if no new chat prompt
94
- if user_prompt:
95
- st.session_state.messages.append({"role": "user", "content": user_prompt})
96
- elif st.session_state.pending_prompt:
97
- st.session_state.messages.append({"role": "user", "content": st.session_state.pending_prompt})
98
  st.session_state.pending_prompt = None
99
 
100
- if st.session_state.messages and st.session_state.messages[-1]["role"] == "user":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  try:
102
- if st.session_state.thread_id is None:
103
  thread = client.beta.threads.create()
104
- st.session_state.thread_id = thread.id
105
 
106
  client.beta.threads.messages.create(
107
- thread_id=st.session_state.thread_id,
108
  role="user",
109
- content=st.session_state.messages[-1]["content"]
110
  )
111
 
112
  run = client.beta.threads.runs.create(
113
- thread_id=st.session_state.thread_id,
114
- assistant_id=ASSISTANT_ID
115
  )
116
 
117
- with st.spinner("πŸ€– Parsing and responding with referenced content..."):
118
  while True:
119
  run_status = client.beta.threads.runs.retrieve(
120
- thread_id=st.session_state.thread_id,
121
  run_id=run.id
122
  )
123
  if run_status.status in ("completed", "failed", "cancelled"):
@@ -127,44 +199,17 @@ with chat_col:
127
  if run_status.status != "completed":
128
  st.error(f"⚠️ Assistant failed: {run_status.status}")
129
  else:
130
- messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
131
  for message in reversed(messages.data):
132
  if message.role == "assistant":
133
- assistant_reply = message.content[0].text.value
134
- st.session_state.messages.append({"role": "assistant", "content": assistant_reply})
135
-
136
- # Parse Document Reference and Page, then construct image URL with encoding
137
- match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', assistant_reply)
138
- if match:
139
- doc_name = match.group(1).strip()
140
- page = int(match.group(2))
141
- page_str = f"{page:04d}"
142
- folder = quote(doc_name)
143
- image_url = (
144
- f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/"
145
- f"{folder}/{folder}_page_{page_str}.png"
146
- )
147
- st.session_state.image_url = image_url
148
- st.session_state.image_updated = True
149
  break
150
 
151
  st.rerun()
152
  except Exception as e:
153
  st.error(f"❌ Error: {e}")
154
 
155
- for msg in st.session_state.messages:
156
  with st.chat_message(msg["role"]):
157
  st.markdown(msg["content"], unsafe_allow_html=True)
158
-
159
- # ------------------ Image Display ------------------
160
- with image_col:
161
- if show_image and st.session_state.image_url:
162
- with st.spinner("Loading document preview..."):
163
- try:
164
- response = requests.get(st.session_state.image_url)
165
- response.raise_for_status()
166
- img = Image.open(BytesIO(response.content))
167
- st.image(img, caption="πŸ“„ OCR Page Image", use_container_width=True)
168
- st.session_state.image_updated = False
169
- except Exception as e:
170
- st.error(f"❗ Failed to load image: {e}")
 
13
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
14
  st.caption("Explore City of Armadale construction documents using AI + OCR 🧠")
15
 
16
+ # ------------------ Load API Key and Assistant IDs ------------------
17
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
18
+ CONTRACT_ASSISTANT_ID = os.environ.get("ASSISTANT_ID") # Contract assistant
19
+ TECH_ASSISTANT_ID = "asst_DjvuWBc7tCvMbAhY7n1em4BZ" # Technical drawings assistant
20
 
21
+ if not OPENAI_API_KEY or not CONTRACT_ASSISTANT_ID:
22
+ st.error("❌ Missing secrets. Please set 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
+ # ------------------ Tabs ------------------
28
+ tab1, tab2 = st.tabs(["πŸ“„ Contract Assistant", "πŸ“ Technical Drawings Assistant"])
29
+
30
+ # ------------------ Tab 1: Contract Assistant ------------------
31
+ with tab1:
32
+ if "messages" not in st.session_state:
33
+ st.session_state.messages = []
34
+ if "thread_id" not in st.session_state:
35
+ st.session_state.thread_id = None
36
+ if "image_url" not in st.session_state:
37
+ st.session_state.image_url = None
38
+ if "image_updated" not in st.session_state:
39
+ st.session_state.image_updated = False
40
+ if "pending_prompt" not in st.session_state:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  st.session_state.pending_prompt = None
42
 
43
+ show_image = st.sidebar.toggle("πŸ“‘ Show Page Image", value=True)
44
+
45
+ st.sidebar.subheader("πŸ“˜ Document Tools")
46
+ keyword = st.sidebar.text_input("Search by Keyword", placeholder="e.g. defects, WHS, delay")
47
+ if st.sidebar.button("πŸ”Ž Search Keyword") and keyword:
48
+ st.session_state.pending_prompt = f"Find clauses or references related to: {keyword}"
49
+
50
+ section_options = [
51
+ "Select a section...",
52
+ "1. Formal Instrument of Contract",
53
+ "2. Offer and Acceptance",
54
+ "3. Key Personnel",
55
+ "4. Contract Pricing",
56
+ "5. Specifications",
57
+ "6. WHS Policies",
58
+ "7. Penalties and Delays",
59
+ "8. Dispute Resolution",
60
+ "9. Principal Obligations"
61
+ ]
62
+ section_select = st.sidebar.selectbox("πŸ“„ Jump to Section", section_options)
63
+ if section_select != section_options[0]:
64
+ st.session_state.pending_prompt = f"Summarize or list key points from section: {section_select}"
65
+
66
+ actions = [
67
+ "Select an action...",
68
+ "List all contractual obligations",
69
+ "Summarize payment terms",
70
+ "List WHS responsibilities",
71
+ "Find delay-related penalties",
72
+ "Extract dispute resolution steps"
73
+ ]
74
+ action_select = st.sidebar.selectbox("βš™οΈ Common Contract Queries", actions)
75
+ if action_select != actions[0]:
76
+ st.session_state.pending_prompt = action_select
77
+
78
+ chat_col, image_col = st.columns([2, 1])
79
+
80
+ with chat_col:
81
+ st.markdown("### 🧠 Ask a Document-Specific Question")
82
+ user_prompt = st.chat_input("Example: What is the defects liability period?")
83
+
84
+ if user_prompt:
85
+ st.session_state.messages.append({"role": "user", "content": user_prompt})
86
+ elif st.session_state.pending_prompt:
87
+ st.session_state.messages.append({"role": "user", "content": st.session_state.pending_prompt})
88
+ st.session_state.pending_prompt = None
89
+
90
+ if st.session_state.messages and st.session_state.messages[-1]["role"] == "user":
91
+ try:
92
+ if st.session_state.thread_id is None:
93
+ thread = client.beta.threads.create()
94
+ st.session_state.thread_id = thread.id
95
+
96
+ client.beta.threads.messages.create(
97
+ thread_id=st.session_state.thread_id,
98
+ role="user",
99
+ content=st.session_state.messages[-1]["content"]
100
+ )
101
+
102
+ run = client.beta.threads.runs.create(
103
+ thread_id=st.session_state.thread_id,
104
+ assistant_id=CONTRACT_ASSISTANT_ID
105
+ )
106
+
107
+ with st.spinner("πŸ€– Parsing and responding with referenced content..."):
108
+ while True:
109
+ run_status = client.beta.threads.runs.retrieve(
110
+ thread_id=st.session_state.thread_id,
111
+ run_id=run.id
112
+ )
113
+ if run_status.status in ("completed", "failed", "cancelled"):
114
+ break
115
+ time.sleep(1)
116
+
117
+ if run_status.status != "completed":
118
+ st.error(f"⚠️ Assistant failed: {run_status.status}")
119
+ else:
120
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
121
+ for message in reversed(messages.data):
122
+ if message.role == "assistant":
123
+ assistant_reply = message.content[0].text.value
124
+ st.session_state.messages.append({"role": "assistant", "content": assistant_reply})
125
+
126
+ match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\d+)', assistant_reply)
127
+ if match:
128
+ doc_name = match.group(1).strip()
129
+ page = int(match.group(2))
130
+ page_str = f"{page:04d}"
131
+ folder = quote(doc_name)
132
+ image_url = (
133
+ f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/"
134
+ f"{folder}/{folder}_page_{page_str}.png"
135
+ )
136
+ st.session_state.image_url = image_url
137
+ st.session_state.image_updated = True
138
+ break
139
+
140
+ st.rerun()
141
+ except Exception as e:
142
+ st.error(f"❌ Error: {e}")
143
+
144
+ for msg in st.session_state.messages:
145
+ with st.chat_message(msg["role"]):
146
+ st.markdown(msg["content"], unsafe_allow_html=True)
147
+
148
+ with image_col:
149
+ if show_image and st.session_state.image_url:
150
+ with st.spinner("Loading document preview..."):
151
+ try:
152
+ response = requests.get(st.session_state.image_url)
153
+ response.raise_for_status()
154
+ img = Image.open(BytesIO(response.content))
155
+ st.image(img, caption="πŸ“„ OCR Page Image", use_container_width=True)
156
+ st.session_state.image_updated = False
157
+ except Exception as e:
158
+ st.error(f"❗ Failed to load image: {e}")
159
+
160
+ # ------------------ Tab 2: Technical Drawing Assistant ------------------
161
+ with tab2:
162
+ if "tech_messages" not in st.session_state:
163
+ st.session_state.tech_messages = []
164
+ st.session_state.tech_thread_id = None
165
+
166
+ st.markdown("### πŸ—οΈ Ask a Question About Drawings or Diagrams")
167
+ tech_prompt = st.chat_input("Example: Show me all architectural drawings")
168
+
169
+ if tech_prompt:
170
+ st.session_state.tech_messages.append({"role": "user", "content": tech_prompt})
171
+
172
+ if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
173
  try:
174
+ if st.session_state.tech_thread_id is None:
175
  thread = client.beta.threads.create()
176
+ st.session_state.tech_thread_id = thread.id
177
 
178
  client.beta.threads.messages.create(
179
+ thread_id=st.session_state.tech_thread_id,
180
  role="user",
181
+ content=st.session_state.tech_messages[-1]["content"]
182
  )
183
 
184
  run = client.beta.threads.runs.create(
185
+ thread_id=st.session_state.tech_thread_id,
186
+ assistant_id=TECH_ASSISTANT_ID
187
  )
188
 
189
+ with st.spinner("πŸ” Querying Technical Drawing Assistant..."):
190
  while True:
191
  run_status = client.beta.threads.runs.retrieve(
192
+ thread_id=st.session_state.tech_thread_id,
193
  run_id=run.id
194
  )
195
  if run_status.status in ("completed", "failed", "cancelled"):
 
199
  if run_status.status != "completed":
200
  st.error(f"⚠️ Assistant failed: {run_status.status}")
201
  else:
202
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.tech_thread_id)
203
  for message in reversed(messages.data):
204
  if message.role == "assistant":
205
+ reply = message.content[0].text.value
206
+ st.session_state.tech_messages.append({"role": "assistant", "content": reply})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  break
208
 
209
  st.rerun()
210
  except Exception as e:
211
  st.error(f"❌ Error: {e}")
212
 
213
+ for msg in st.session_state.tech_messages:
214
  with st.chat_message(msg["role"]):
215
  st.markdown(msg["content"], unsafe_allow_html=True)