IAMTFRMZA commited on
Commit
717ae43
Β·
verified Β·
1 Parent(s): 9910527

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -58
app.py CHANGED
@@ -1,95 +1,170 @@
 
1
  import os
2
- import json
3
  import time
 
4
  import requests
5
- import streamlit as st
 
 
6
  from openai import OpenAI
7
 
8
- # -------------------- CONFIG --------------------
 
 
 
 
 
9
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
10
  ASSISTANT_ID = os.environ.get("ASSISTANT_ID")
11
 
12
- st.set_page_config(page_title="Forrestdale Technical Drawing Assistant", layout="wide")
13
- st.title("🧱 Forrestdale Technical Drawing Assistant")
14
-
15
- # -------------------- ERROR CHECK --------------------
16
  if not OPENAI_API_KEY or not ASSISTANT_ID:
17
- st.error("❌ Missing API credentials. Please set OPENAI_API_KEY and ASSISTANT_ID as environment variables.")
18
  st.stop()
19
 
20
  client = OpenAI(api_key=OPENAI_API_KEY)
21
 
22
- # -------------------- CHAT SESSION --------------------
23
  if "messages" not in st.session_state:
24
  st.session_state.messages = []
25
  if "thread_id" not in st.session_state:
26
  st.session_state.thread_id = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- query = st.text_input("Ask about plans, drawings or components", placeholder="e.g. Show me all electrical plans")
 
 
29
 
30
- if query:
31
- with st.spinner("Querying assistant..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  try:
33
- # Create thread if needed
34
- if not st.session_state.thread_id:
35
  thread = client.beta.threads.create()
36
  st.session_state.thread_id = thread.id
37
 
38
- # Submit message
39
  client.beta.threads.messages.create(
40
  thread_id=st.session_state.thread_id,
41
  role="user",
42
- content=query
43
  )
44
 
45
- # Run assistant
46
  run = client.beta.threads.runs.create(
47
  thread_id=st.session_state.thread_id,
48
  assistant_id=ASSISTANT_ID
49
  )
50
 
51
- # Poll for result
52
- while True:
53
- run_status = client.beta.threads.runs.retrieve(
54
- thread_id=st.session_state.thread_id,
55
- run_id=run.id
56
- )
57
- if run_status.status == "completed":
58
- break
59
- elif run_status.status in ("failed", "cancelled"):
60
- raise Exception(f"Assistant failed: {run_status.status}")
61
- time.sleep(1)
62
-
63
- # Fetch assistant message
64
- messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
65
- for message in reversed(messages.data):
66
- if message.role == "assistant":
67
- content = message.content[0].text.value.strip()
68
- if content.startswith("```json") and content.endswith("```"):
69
- json_block = content.strip("```json\n").strip("```")
70
- parsed = json.loads(json_block)
71
- st.session_state.messages.append(parsed)
72
- else:
73
- st.error("⚠️ Could not parse assistant response as JSON.")
74
- break
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  except Exception as e:
77
  st.error(f"❌ Error: {e}")
78
 
79
- # -------------------- DISPLAY RESULTS --------------------
80
- if st.session_state.messages:
81
- drawings = st.session_state.messages[-1]
82
- if isinstance(drawings, list):
83
- cols = st.columns(4)
84
- for idx, item in enumerate(drawings):
85
- with cols[idx % 4]:
86
- st.subheader(f"πŸ“˜ {item['drawing_number']}")
87
- st.caption(f"Discipline: {item['discipline']}")
88
- st.write(item.get("summary", "No summary available."))
89
- if "images" in item:
90
- for img in item["images"][:1]:
91
- st.image(img, use_column_width=True)
92
- elif "image" in item:
93
- st.image(item["image"], use_column_width=True)
94
- if "question" in item:
95
- st.markdown(f"**Related Question:** {item['question']}")
 
1
+ import streamlit as st
2
  import os
 
3
  import time
4
+ import re
5
  import requests
6
+ from PIL import Image
7
+ from io import BytesIO
8
+ from urllib.parse import quote
9
  from openai import OpenAI
10
 
11
+ # ------------------ App Configuration ------------------
12
+ st.set_page_config(page_title="Schlaeger Forrestdale DocAIA", layout="wide", initial_sidebar_state="collapsed")
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"):
124
+ break
125
+ time.sleep(1)
126
+
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}")