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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -90
app.py CHANGED
@@ -5,12 +5,13 @@ import re
5
  import requests
6
  from PIL import Image
7
  from io import BytesIO
 
8
  from openai import OpenAI
9
 
10
  # ------------------ App Configuration ------------------
11
- st.set_page_config(page_title="Schlaeger Forrestdale DocAIA", layout="wide")
12
  st.title("πŸ“„ Schlaeger Forrestdale Document Assistant")
13
- st.caption("Explore technical construction documents via AI with OCR references")
14
 
15
  # ------------------ Load API Key and Assistant ID ------------------
16
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
@@ -31,30 +32,27 @@ if "image_url" not in st.session_state:
31
  st.session_state.image_url = None
32
  if "image_updated" not in st.session_state:
33
  st.session_state.image_updated = False
34
- if "search_history" not in st.session_state:
35
- st.session_state.search_history = []
36
- if "run_in_progress" not in st.session_state:
37
- st.session_state.run_in_progress = False
38
 
39
- # ------------------ Sidebar (Left Utility Panel) ------------------
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.image_url = None
45
  st.session_state.image_updated = False
46
- st.session_state.search_history = []
47
- st.session_state.run_in_progress = False
48
  st.rerun()
49
 
50
  show_image = st.sidebar.toggle("πŸ“‘ Show Page Image", value=True)
51
 
52
- st.sidebar.markdown("### πŸ” Navigate or Query Document")
53
- keyword = st.sidebar.text_input("Jump to clause keyword or term", placeholder="e.g. defects, payment, WHS")
 
 
54
  if st.sidebar.button("πŸ”Ž Search Keyword") and keyword:
55
- st.session_state.search_history.append(keyword)
56
- if not st.session_state.run_in_progress:
57
- st.session_state.messages.append({"role": "user", "content": f"Find clauses or references related to: {keyword}"})
58
 
59
  section_options = [
60
  "Select a section...",
@@ -62,15 +60,15 @@ section_options = [
62
  "2. Offer and Acceptance",
63
  "3. Key Personnel",
64
  "4. Contract Pricing",
65
- "5. Contract Specifications",
66
  "6. WHS Policies",
67
  "7. Penalties and Delays",
68
  "8. Dispute Resolution",
69
  "9. Principal Obligations"
70
  ]
71
- selected_section = st.sidebar.selectbox("πŸ“„ Browse Sections", section_options)
72
- if selected_section and selected_section != section_options[0]:
73
- st.session_state.messages.append({"role": "user", "content": f"Summarize or list key points from section: {selected_section}"})
74
 
75
  actions = [
76
  "Select an action...",
@@ -80,75 +78,93 @@ actions = [
80
  "Find delay-related penalties",
81
  "Extract dispute resolution steps"
82
  ]
83
- selected_action = st.sidebar.selectbox("βš™οΈ Common Actions", actions)
84
- if selected_action and selected_action != actions[0]:
85
- st.session_state.messages.append({"role": "user", "content": selected_action})
86
-
87
- if st.session_state.search_history:
88
- st.sidebar.markdown("---")
89
- st.sidebar.markdown("#### πŸ” Recent Searches")
90
- for term in reversed(st.session_state.search_history[-5:]):
91
- st.sidebar.markdown(f"- {term}")
92
-
93
- if show_image and st.session_state.image_url:
94
- st.sidebar.markdown("---")
95
- st.sidebar.markdown("#### πŸ–ΌοΈ Page Image (Toggle)")
96
- try:
97
- response = requests.get(st.session_state.image_url)
98
- response.raise_for_status()
99
- img = Image.open(BytesIO(response.content))
100
- st.sidebar.image(img, caption="OCR Page", use_container_width=True)
101
- except Exception as e:
102
- st.sidebar.error(f"❗ Failed to load image: {e}")
103
-
104
- # ------------------ Center Panel: Chat ------------------
105
- st.markdown("### 🧠 Ask a Document-Specific Question")
106
- prompt = st.chat_input("Example: What is the defects liability period?")
107
-
108
- for msg in st.session_state.messages:
109
- with st.chat_message(msg["role"]):
110
- st.markdown(msg["content"], unsafe_allow_html=True)
111
-
112
- if prompt and not st.session_state.run_in_progress:
113
- st.session_state.messages.append({"role": "user", "content": prompt})
114
- st.session_state.run_in_progress = True
115
-
116
- try:
117
- if st.session_state.thread_id is None:
118
- thread = client.beta.threads.create()
119
- st.session_state.thread_id = thread.id
120
-
121
- client.beta.threads.messages.create(thread_id=st.session_state.thread_id, role="user", content=prompt)
122
- run = client.beta.threads.runs.create(thread_id=st.session_state.thread_id, assistant_id=ASSISTANT_ID)
123
-
124
- with st.spinner("πŸ€– Parsing and responding with referenced content..."):
125
- while True:
126
- run_status = client.beta.threads.runs.retrieve(thread_id=st.session_state.thread_id, run_id=run.id)
127
- if run_status.status in ("completed", "failed", "cancelled"):
128
- break
129
- time.sleep(1)
130
-
131
- if run_status.status != "completed":
132
- st.error(f"⚠️ Assistant failed: {run_status.status}")
133
- else:
134
- messages = client.beta.threads.messages.list(thread_id=st.session_state.thread_id)
135
- for message in reversed(messages.data):
136
- if message.role == "assistant":
137
- assistant_message = message.content[0].text.value
138
- st.session_state.messages.append({"role": "assistant", "content": assistant_message})
139
- break
140
-
141
- match = re.search(r'Document Reference:\s+(.+?),\s+Page\s+(\d+)', assistant_message)
142
- if match:
143
- doc_name = match.group(1).strip()
144
- page = int(match.group(2))
145
- page_str = f"{page:04d}"
146
- image_url = f"https://raw.githubusercontent.com/AndrewLORTech/c2ozschlaegerforrestdale/main/{doc_name}/{doc_name}_page_{page_str}.png"
147
- st.session_state.image_url = image_url
148
- st.session_state.image_updated = True
149
-
150
- st.session_state.run_in_progress = False
151
- st.rerun()
152
- except Exception as e:
153
- st.session_state.run_in_progress = False
154
- st.error(f"❌ Error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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")
 
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...",
 
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...",
 
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}")