IAMTFRMZA commited on
Commit
f1f3914
Β·
verified Β·
1 Parent(s): a4a4e16

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -108
app.py CHANGED
@@ -1,132 +1,103 @@
1
  import streamlit as st
 
2
  import os
3
- import time
4
  import re
5
  import requests
6
- import json
7
  from PIL import Image
8
  from io import BytesIO
9
  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
- 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
- if st.button("πŸ–ΌοΈ View Image", key=f"view_{i}"):
121
- st.session_state.lightbox_url = url
122
-
123
- if st.session_state.lightbox_url:
124
- st.markdown("---")
125
- st.image(st.session_state.lightbox_url, use_column_width=True, caption="πŸ” Enlarged Preview")
126
- if st.button("❌ Close Viewer"):
127
- st.session_state.lightbox_url = None
128
- st.rerun()
129
- else:
130
- for msg in st.session_state.messages:
131
- with st.chat_message(msg["role"]):
132
- st.markdown(msg["content"], unsafe_allow_html=True)
 
1
  import streamlit as st
2
+ import openai
3
  import os
4
+ import json
5
  import re
6
  import requests
 
7
  from PIL import Image
8
  from io import BytesIO
9
  from urllib.parse import quote
 
10
 
11
+ # ------------------ Configuration ------------------
12
+ st.set_page_config(page_title="Forrestdale Technical Drawing Assistant", layout="wide")
13
+ st.markdown("""
14
+ <h1 style='font-size: 2.5rem;'>πŸͺ Forrestdale Technical Drawing Assistant</h1>
15
+ <p style='font-size: 1rem;'>Ask about plans, drawings or components</p>
16
+ """, unsafe_allow_html=True)
17
 
18
+ # ------------------ Environment ------------------
19
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
20
+ ASSISTANT_ID = os.getenv("ASSISTANT_ID")
21
 
22
  if not OPENAI_API_KEY or not ASSISTANT_ID:
23
+ st.error("❌ Missing environment variables: OPENAI_API_KEY or ASSISTANT_ID.")
24
  st.stop()
25
 
26
+ client = openai.OpenAI(api_key=OPENAI_API_KEY)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # ------------------ Helper Functions ------------------
29
+ def extract_json_from_response(response_text):
 
 
 
 
 
30
  try:
31
+ match = re.search(r"```json\s*([\s\S]+?)```", response_text.strip())
32
+ if not match:
33
+ raise ValueError("No valid JSON block found.")
34
+ json_str = match.group(1).strip()
35
+ return json.loads(json_str)
36
+ except Exception as e:
37
+ st.error("⚠️ Could not parse assistant response as JSON.")
38
+ st.stop()
39
+
40
+ def display_card(item):
41
+ st.markdown("""
42
+ <div style="border: 1px solid #333; border-radius: 12px; padding: 16px; margin: 10px; background-color: #111;">
43
+ <h3>πŸ“ {} ({})</h3>
44
+ <p><b>Discipline:</b> {}</p>
45
+ <p><b>Summary:</b> {}</p>
46
+ <div style="display: flex; flex-wrap: wrap; gap: 10px;">
47
+ """.format(item["drawing_number"], item.get("drawing_type", ""), item["discipline"], item["summary"]), unsafe_allow_html=True)
48
+
49
+ for img in item.get("images", []):
50
+ try:
51
+ response = requests.get(img)
52
+ if response.status_code == 200:
53
+ image = Image.open(BytesIO(response.content))
54
+ st.image(image, caption=os.path.basename(img), width=200)
55
+ except:
56
+ st.warning(f"Image failed to load: {img}")
57
+
58
+ st.markdown("</div></div>", unsafe_allow_html=True)
59
+
60
+ # ------------------ Input ------------------
61
+ query = st.text_input("Ask about plans, drawings or components", placeholder="Show me all architectural plans")
62
+
63
+ if query:
64
+ with st.spinner("πŸ” Querying assistant..."):
65
+ try:
66
+ if "thread_id" not in st.session_state:
67
+ thread = client.beta.threads.create()
68
+ st.session_state.thread_id = thread.id
69
+
70
+ client.beta.threads.messages.create(
71
+ thread_id=st.session_state.thread_id,
72
+ role="user",
73
+ content=query
74
+ )
75
+
76
+ run = client.beta.threads.runs.create(
77
+ thread_id=st.session_state.thread_id,
78
+ assistant_id=ASSISTANT_ID
79
+ )
80
 
 
81
  while True:
82
  run_status = client.beta.threads.runs.retrieve(
83
  thread_id=st.session_state.thread_id,
84
  run_id=run.id
85
  )
86
+ if run_status.status in ["completed", "failed", "cancelled"]:
87
  break
88
  time.sleep(1)
89
 
90
+ if run_status.status != "completed":
91
+ st.error(f"❌ Assistant run failed: {run_status.status}")
92
+ else:
93
+ messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
94
+ for message in reversed(messages.data):
95
+ if message.role == "assistant":
96
+ response_text = message.content[0].text.value
97
+ data = extract_json_from_response(response_text)
98
+ for item in data:
99
+ display_card(item)
100
+ break
101
+
102
+ except Exception as e:
103
+ st.error(f"❌ Error during assistant call: {e}")