IAMTFRMZA commited on
Commit
1fecdb8
Β·
verified Β·
1 Parent(s): 8614885

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -100
app.py CHANGED
@@ -10,112 +10,175 @@ from urllib.parse import quote
10
  from openai import OpenAI
11
 
12
  # ------------------ App Configuration ------------------
13
- st.set_page_config(page_title="Schlaeger Forrestdale TechDocAIA", layout="wide", initial_sidebar_state="collapsed")
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
-
37
- # ------------------ Sidebar ------------------
38
- st.sidebar.header("ℹ️ Information")
39
- if st.sidebar.button("🧹 Clear Chat"):
40
- st.session_state.messages = []
41
- st.session_state.thread_id = None
42
- st.session_state.pending_prompt = None
43
- st.session_state.results = []
44
- st.rerun()
45
-
46
- # ------------------ Chat Input ------------------
47
- user_prompt = st.chat_input("Ask about plans, drawings or components")
48
- if user_prompt:
49
- st.session_state.messages.append({"role": "user", "content": user_prompt})
50
-
51
- # ------------------ Assistant Query ------------------
52
- if st.session_state.messages and st.session_state.messages[-1]["role"] == "user":
53
- try:
54
- if st.session_state.thread_id is None:
55
- thread = client.beta.threads.create()
56
- st.session_state.thread_id = thread.id
57
-
58
- client.beta.threads.messages.create(
59
- thread_id=st.session_state.thread_id,
60
- role="user",
61
- content=st.session_state.messages[-1]["content"]
62
- )
63
-
64
- run = client.beta.threads.runs.create(
65
- thread_id=st.session_state.thread_id,
66
- assistant_id=ASSISTANT_ID
67
- )
68
-
69
- with st.spinner("πŸ€– Parsing and responding..."):
70
- while True:
71
- run_status = client.beta.threads.runs.retrieve(
72
- thread_id=st.session_state.thread_id,
73
- run_id=run.id
74
- )
75
- if run_status.status in ("completed", "failed", "cancelled"):
76
- break
77
- time.sleep(1)
78
-
79
- if run_status.status != "completed":
80
- st.error(f"⚠️ Assistant failed: {run_status.status}")
81
- else:
82
- messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
83
- for message in reversed(messages.data):
84
- if message.role == "assistant":
85
- assistant_reply = message.content[0].text.value
86
- st.session_state.messages.append({"role": "assistant", "content": assistant_reply})
87
- try:
88
- json_data = json.loads(assistant_reply.strip("`json "))
89
- st.session_state.results = json_data
90
- except:
91
- st.session_state.results = []
92
- break
93
- st.rerun()
94
- except Exception as e:
95
- st.error(f"❌ Error: {e}")
96
-
97
- # ------------------ Sorting and Filters ------------------
98
- if st.session_state.results:
99
- disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.results))
100
- selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
101
- page_size = 8
102
- page_num = st.number_input("Page", min_value=1, step=1, value=1)
103
-
104
- filtered_results = [r for r in st.session_state.results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
105
- paged = filtered_results[(page_num - 1) * page_size : page_num * page_size]
106
-
107
- st.markdown("---")
108
- st.subheader("πŸ“‚ Drawing Results")
109
- cols = st.columns(4)
110
-
111
- for i, item in enumerate(paged):
112
- with cols[i % 4]:
113
- st.markdown(f"**{item['drawing_number']}**")
114
- st.markdown(f"_Discipline: {item['discipline']}_")
115
- st.caption(item.get("summary", ""))
116
- for url in item.get("images", [])[:1]:
117
- st.image(url, caption=f"{item['drawing_number']} – Page 1", use_column_width=True)
118
- else:
119
- for msg in st.session_state.messages:
120
  with st.chat_message(msg["role"]):
121
  st.markdown(msg["content"], unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  from openai import OpenAI
11
 
12
  # ------------------ App Configuration ------------------
13
+ st.set_page_config(page_title="Schlaeger Forrestdale DocAIA", layout="wide", initial_sidebar_state="collapsed")
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 in environment variables.")
 
 
21
  st.stop()
22
 
23
  client = OpenAI(api_key=OPENAI_API_KEY)
24
 
25
+ # ------------------ Tabs ------------------
26
+ tab1, tab2 = st.tabs(["πŸ“‘ Contract", "πŸ“ Technical"])
27
+
28
+ # ------------------ Contract Assistant ------------------
29
+ with tab1:
30
+ ASSISTANT_ID = "asst_KsQRedoJUnEeStzfox1o06lO"
31
+
32
+ if "contract_messages" not in st.session_state:
33
+ st.session_state.contract_messages = []
34
+ if "contract_thread_id" not in st.session_state:
35
+ st.session_state.contract_thread_id = None
36
+ if "contract_image_url" not in st.session_state:
37
+ st.session_state.contract_image_url = None
38
+
39
+ st.markdown("### 🧠 Ask a Contract-Specific Question")
40
+ contract_prompt = st.chat_input("Example: What is the defects liability period?", key="contract_input")
41
+
42
+ if contract_prompt:
43
+ st.session_state.contract_messages.append({"role": "user", "content": contract_prompt})
44
+
45
+ if st.session_state.contract_messages and st.session_state.contract_messages[-1]["role"] == "user":
46
+ try:
47
+ if st.session_state.contract_thread_id is None:
48
+ thread = client.beta.threads.create()
49
+ st.session_state.contract_thread_id = thread.id
50
+
51
+ client.beta.threads.messages.create(
52
+ thread_id=st.session_state.contract_thread_id,
53
+ role="user",
54
+ content=st.session_state.contract_messages[-1]["content"]
55
+ )
56
+
57
+ run = client.beta.threads.runs.create(
58
+ thread_id=st.session_state.contract_thread_id,
59
+ assistant_id=ASSISTANT_ID
60
+ )
61
+
62
+ with st.spinner("πŸ€– Parsing contract documents..."):
63
+ while True:
64
+ run_status = client.beta.threads.runs.retrieve(
65
+ thread_id=st.session_state.contract_thread_id,
66
+ run_id=run.id
67
+ )
68
+ if run_status.status in ("completed", "failed", "cancelled"):
69
+ break
70
+ time.sleep(1)
71
+
72
+ if run_status.status == "completed":
73
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.contract_thread_id)
74
+ for message in reversed(messages.data):
75
+ if message.role == "assistant":
76
+ reply = message.content[0].text.value
77
+ st.session_state.contract_messages.append({"role": "assistant", "content": reply})
78
+
79
+ match = re.search(r'Document Reference:\s*(.*?),\s*Page\s*(\\d+)', reply)
80
+ if match:
81
+ doc_name = match.group(1).strip()
82
+ page = int(match.group(2))
83
+ page_str = f"{page:04d}"
84
+ folder = quote(doc_name)
85
+ url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{folder}/{folder}_page_{page_str}.png"
86
+ st.session_state.contract_image_url = url
87
+ break
88
+ else:
89
+ st.error(f"❌ Contract Assistant failed: {run_status.status}")
90
+ st.rerun()
91
+ except Exception as e:
92
+ st.error(f"❌ Error: {e}")
93
+
94
+ for msg in st.session_state.contract_messages:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  with st.chat_message(msg["role"]):
96
  st.markdown(msg["content"], unsafe_allow_html=True)
97
+
98
+ if st.session_state.contract_image_url:
99
+ st.image(st.session_state.contract_image_url, caption="πŸ“„ Contract Page Image", use_container_width=True)
100
+
101
+ # ------------------ Technical Drawing Assistant ------------------
102
+ with tab2:
103
+ ASSISTANT_ID = "asst_DjvuWBc7tCvMbAhY7n1em4BZ"
104
+
105
+ if "tech_messages" not in st.session_state:
106
+ st.session_state.tech_messages = []
107
+ if "tech_thread_id" not in st.session_state:
108
+ st.session_state.tech_thread_id = None
109
+ if "tech_results" not in st.session_state:
110
+ st.session_state.tech_results = []
111
+
112
+ tech_prompt = st.chat_input("Ask about plans, drawings or components", key="tech_input")
113
+ if tech_prompt:
114
+ st.session_state.tech_messages.append({"role": "user", "content": tech_prompt})
115
+
116
+ if st.session_state.tech_messages and st.session_state.tech_messages[-1]["role"] == "user":
117
+ try:
118
+ if st.session_state.tech_thread_id is None:
119
+ thread = client.beta.threads.create()
120
+ st.session_state.tech_thread_id = thread.id
121
+
122
+ client.beta.threads.messages.create(
123
+ thread_id=st.session_state.tech_thread_id,
124
+ role="user",
125
+ content=st.session_state.tech_messages[-1]["content"]
126
+ )
127
+
128
+ run = client.beta.threads.runs.create(
129
+ thread_id=st.session_state.tech_thread_id,
130
+ assistant_id=ASSISTANT_ID
131
+ )
132
+
133
+ with st.spinner("πŸ€– Searching technical drawings..."):
134
+ while True:
135
+ run_status = client.beta.threads.runs.retrieve(
136
+ thread_id=st.session_state.tech_thread_id,
137
+ run_id=run.id
138
+ )
139
+ if run_status.status in ("completed", "failed", "cancelled"):
140
+ break
141
+ time.sleep(1)
142
+
143
+ if run_status.status == "completed":
144
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.tech_thread_id)
145
+ for message in reversed(messages.data):
146
+ if message.role == "assistant":
147
+ reply = message.content[0].text.value
148
+ st.session_state.tech_messages.append({"role": "assistant", "content": reply})
149
+ try:
150
+ json_data = json.loads(reply.strip("`json "))
151
+ st.session_state.tech_results = json_data
152
+ except:
153
+ st.session_state.tech_results = []
154
+ break
155
+ else:
156
+ st.error(f"❌ Technical Assistant failed: {run_status.status}")
157
+ st.rerun()
158
+ except Exception as e:
159
+ st.error(f"❌ Error: {e}")
160
+
161
+ # ------------------ Filters and Display ------------------
162
+ if st.session_state.tech_results:
163
+ disciplines = sorted(set(d.get("discipline", "") for d in st.session_state.tech_results))
164
+ selected_discipline = st.selectbox("🌍 Filter by discipline", ["All"] + disciplines)
165
+ page_size = 8
166
+ page_num = st.number_input("Page", min_value=1, step=1, value=1)
167
+
168
+ filtered = [r for r in st.session_state.tech_results if selected_discipline == "All" or r.get("discipline") == selected_discipline]
169
+ paged = filtered[(page_num - 1) * page_size : page_num * page_size]
170
+
171
+ st.markdown("---")
172
+ st.subheader("πŸ“‚ Drawing Results")
173
+ cols = st.columns(4)
174
+ for i, item in enumerate(paged):
175
+ with cols[i % 4]:
176
+ st.markdown(f"**{item['drawing_number']}**")
177
+ st.markdown(f"_Discipline: {item['discipline']}_")
178
+ st.caption(item.get("summary", ""))
179
+ for url in item.get("images", [])[:1]:
180
+ st.image(url, caption="Thumbnail", use_container_width=True)
181
+ else:
182
+ for msg in st.session_state.tech_messages:
183
+ with st.chat_message(msg["role"]):
184
+ st.markdown(msg["content"], unsafe_allow_html=True)