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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -161
app.py CHANGED
@@ -10,175 +10,116 @@ from urllib.parse import quote
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)
 
 
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
+ 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)