Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -4,11 +4,12 @@ import streamlit as st
|
|
4 |
from PIL import Image
|
5 |
from openai import OpenAI
|
6 |
from pathlib import Path
|
7 |
-
|
8 |
|
9 |
# β
1. Page Configuration β MUST be first Streamlit command
|
10 |
st.set_page_config(page_title="AI Architecture Assistant", layout="centered")
|
11 |
|
|
|
12 |
# β
2. Inline custom CSS with Poppins font and olive green theme
|
13 |
st.markdown("""
|
14 |
<style>
|
@@ -169,6 +170,324 @@ if st.button("Get Material Recommendations"):
|
|
169 |
st.markdown(result)
|
170 |
else:
|
171 |
st.warning("Please enter a prompt first.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
|
174 |
|
|
|
4 |
from PIL import Image
|
5 |
from openai import OpenAI
|
6 |
from pathlib import Path
|
7 |
+
|
8 |
|
9 |
# β
1. Page Configuration β MUST be first Streamlit command
|
10 |
st.set_page_config(page_title="AI Architecture Assistant", layout="centered")
|
11 |
|
12 |
+
|
13 |
# β
2. Inline custom CSS with Poppins font and olive green theme
|
14 |
st.markdown("""
|
15 |
<style>
|
|
|
170 |
st.markdown(result)
|
171 |
else:
|
172 |
st.warning("Please enter a prompt first.")
|
173 |
+
st.markdown("### π° Cost Estimate by Region")
|
174 |
+
|
175 |
+
region = st.selectbox("Select a region in Canada:", [
|
176 |
+
"Alberta", "British Columbia", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
|
177 |
+
])
|
178 |
+
|
179 |
+
if st.button("Get Cost Estimate"):
|
180 |
+
if st.session_state.prompt.strip():
|
181 |
+
with st.spinner("Calculating cost estimate..."):
|
182 |
+
def generate_cost_estimate(prompt, region):
|
183 |
+
system_instruction = (
|
184 |
+
"You are a residential construction cost estimator for Canadian regions. "
|
185 |
+
"Based on the given architectural prompt and selected region, estimate a typical low-to-high cost range "
|
186 |
+
"for building the described design. Mention 4β5 key cost categories. Use Canadian dollars. "
|
187 |
+
"Adjust prices to be realistic for the selected region, and keep your response brief and clear."
|
188 |
+
)
|
189 |
+
|
190 |
+
user_input = f"Region: {region}\n\nPrompt:\n{prompt}"
|
191 |
+
|
192 |
+
response = client.chat.completions.create(
|
193 |
+
model="gpt-4",
|
194 |
+
messages=[
|
195 |
+
{"role": "system", "content": system_instruction},
|
196 |
+
{"role": "user", "content": user_input}
|
197 |
+
],
|
198 |
+
temperature=0.7
|
199 |
+
)
|
200 |
+
return response.choices[0].message.content.strip()
|
201 |
+
|
202 |
+
estimate = generate_cost_estimate(st.session_state.prompt, region)
|
203 |
+
st.markdown(estimate)
|
204 |
+
else:
|
205 |
+
st.warning("Please enter a prompt first.")
|
206 |
+
st.markdown("### ποΈ Building Code Comparison")
|
207 |
+
|
208 |
+
st.markdown("---")
|
209 |
+
st.markdown("### π§Ύ Building Code Comparison Between Provinces")
|
210 |
+
|
211 |
+
province_1 = st.selectbox("Select first province:", [
|
212 |
+
"British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
|
213 |
+
], key="prov1")
|
214 |
+
|
215 |
+
province_2 = st.selectbox("Select second province:", [
|
216 |
+
"British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
|
217 |
+
], key="prov2")
|
218 |
+
|
219 |
+
if st.button("Compare Building Codes"):
|
220 |
+
if province_1 != province_2:
|
221 |
+
with st.spinner("Comparing codes across provinces..."):
|
222 |
+
def compare_building_codes(p1, p2, prompt):
|
223 |
+
system_instruction = (
|
224 |
+
"You are a Canadian building code expert. Compare the key differences in building code requirements "
|
225 |
+
f"between {p1} and {p2} for residential construction, especially related to small or modern homes like the one described below. "
|
226 |
+
"Focus on differences in energy efficiency, foundations, insulation, accessibility, and design limitations. "
|
227 |
+
"Keep your response clear and under 10 bullet points."
|
228 |
+
)
|
229 |
+
|
230 |
+
response = client.chat.completions.create(
|
231 |
+
model="gpt-4",
|
232 |
+
messages=[
|
233 |
+
{"role": "system", "content": system_instruction},
|
234 |
+
{"role": "user", "content": prompt}
|
235 |
+
],
|
236 |
+
temperature=0.7
|
237 |
+
)
|
238 |
+
return response.choices[0].message.content.strip()
|
239 |
+
|
240 |
+
comparison = compare_building_codes(province_1, province_2, st.session_state.prompt)
|
241 |
+
st.markdown(f"### ποΈ {province_1} vs. {province_2}")
|
242 |
+
st.markdown(comparison)
|
243 |
+
else:
|
244 |
+
st.warning("Please select two different provinces.")
|
245 |
+
st.markdown("### β‘οΈ What would you like to do next?")
|
246 |
+
|
247 |
+
# === DESIGN IDEAS SECTION ===
|
248 |
+
|
249 |
+
st.markdown("### β‘οΈ What would you like to do next?")
|
250 |
+
|
251 |
+
next_step = st.selectbox(
|
252 |
+
"Now that you have some information and a layout in mind, would you like help with...",
|
253 |
+
[
|
254 |
+
"Select an option",
|
255 |
+
"π¨ Explore design ideas (color palettes, materials for your climate)",
|
256 |
+
"π Continue researching (codes, build types, regulations)"
|
257 |
+
]
|
258 |
+
)
|
259 |
+
|
260 |
+
if next_step == "π¨ Explore design ideas (color palettes, materials for your climate)":
|
261 |
+
st.markdown("#### π¨ Design Recommendations")
|
262 |
+
st.markdown("We'll give you smart and stylish ideas based on your region and layout.")
|
263 |
+
|
264 |
+
# Select region
|
265 |
+
design_region = st.selectbox("Where will your home be built?", [
|
266 |
+
"British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
|
267 |
+
], key="design_region")
|
268 |
+
|
269 |
+
# Select style
|
270 |
+
design_style = st.selectbox("What style do you like?", [
|
271 |
+
"Scandinavian", "Modern Minimalist", "Rustic Cabin", "Japandi", "West Coast Contemporary"
|
272 |
+
], key="design_style")
|
273 |
+
|
274 |
+
# Select design advice categories
|
275 |
+
selected_categories = st.multiselect(
|
276 |
+
"What kind of design advice would you like?",
|
277 |
+
[
|
278 |
+
"Climate-Responsive Material Suggestions",
|
279 |
+
"Interior Color Palette",
|
280 |
+
"Lighting & Window Placement Tips",
|
281 |
+
"Space-Saving Built-ins"
|
282 |
+
],
|
283 |
+
default=["Climate-Responsive Material Suggestions", "Interior Color Palette"]
|
284 |
+
)
|
285 |
+
|
286 |
+
# Generate design ideas with GPT
|
287 |
+
if st.button("Generate Design Ideas"):
|
288 |
+
with st.spinner("Designing your dream space..."):
|
289 |
+
|
290 |
+
def generate_design_ideas(prompt, region, style, categories):
|
291 |
+
system_instruction = (
|
292 |
+
"You are a residential design assistant. Based on the home description, region, and preferred design style, "
|
293 |
+
"give tailored recommendations for the selected categories. Keep it concise, stylish, and easy to implement for a small-scale home."
|
294 |
+
)
|
295 |
+
|
296 |
+
user_input = (
|
297 |
+
f"Prompt:\n{prompt}\n\n"
|
298 |
+
f"Region: {region}\n"
|
299 |
+
f"Style: {style}\n"
|
300 |
+
f"Categories: {', '.join(categories)}"
|
301 |
+
)
|
302 |
+
|
303 |
+
response = client.chat.completions.create(
|
304 |
+
model="gpt-4",
|
305 |
+
messages=[
|
306 |
+
{"role": "system", "content": system_instruction},
|
307 |
+
{"role": "user", "content": user_input}
|
308 |
+
],
|
309 |
+
temperature=0.7
|
310 |
+
)
|
311 |
+
return response.choices[0].message.content.strip()
|
312 |
+
|
313 |
+
design_results = generate_design_ideas(
|
314 |
+
st.session_state.prompt,
|
315 |
+
design_region,
|
316 |
+
design_style,
|
317 |
+
selected_categories
|
318 |
+
)
|
319 |
+
|
320 |
+
st.markdown("### π‘ Design Suggestions")
|
321 |
+
st.markdown(design_results)
|
322 |
+
|
323 |
+
# Store summary in session state
|
324 |
+
st.session_state.design_summary = {
|
325 |
+
"Prompt": st.session_state.prompt,
|
326 |
+
"Region": design_region,
|
327 |
+
"Style": design_style,
|
328 |
+
"Categories": selected_categories,
|
329 |
+
"Design Suggestions": design_results,
|
330 |
+
"Materials": st.session_state.get("material_recommendations", "Not generated"),
|
331 |
+
"Image URL": st.session_state.get("generated_image_url", "Not generated")
|
332 |
+
}
|
333 |
+
|
334 |
+
# === Download Summary ===
|
335 |
+
import io
|
336 |
+
|
337 |
+
st.markdown("---")
|
338 |
+
st.markdown("### π₯ Download Your Concept Summary")
|
339 |
+
|
340 |
+
if st.button("Download Summary as Text"):
|
341 |
+
summary = st.session_state.design_summary
|
342 |
+
content = f"""π AI Architecture Design Summary
|
343 |
+
Prompt:
|
344 |
+
{summary['Prompt']}
|
345 |
+
Region: {summary['Region']}
|
346 |
+
Style: {summary['Style']}
|
347 |
+
Selected Categories: {', '.join(summary['Categories'])}
|
348 |
+
π¨ Design Suggestions:
|
349 |
+
{summary['Design Suggestions']}
|
350 |
+
π§± Recommended Materials:
|
351 |
+
{summary['Materials']}
|
352 |
+
πΌοΈ Floor Plan Image:
|
353 |
+
{summary['Image URL']}
|
354 |
+
"""
|
355 |
+
|
356 |
+
st.download_button(
|
357 |
+
label="Download Summary",
|
358 |
+
data=io.StringIO(content),
|
359 |
+
file_name="design_summary.txt",
|
360 |
+
mime="text/plain"
|
361 |
+
)
|
362 |
+
|
363 |
+
# === Concept Image Generation ===
|
364 |
+
st.markdown("### πΌοΈ Visualizing Your Design Concept")
|
365 |
+
|
366 |
+
if st.button("Generate Concept Image from Design Summary"):
|
367 |
+
with st.spinner("Creating a personalized visual..."):
|
368 |
+
refined_prompt = (
|
369 |
+
f"Based on the following tiny home project details:\n\n"
|
370 |
+
f"Region: {design_region}\n"
|
371 |
+
f"Style: {design_style}\n"
|
372 |
+
f"Categories: {', '.join(selected_categories)}\n"
|
373 |
+
f"Design Suggestions: {st.session_state.design_summary['Design Suggestions']}\n\n"
|
374 |
+
"Create a professional architectural rendering that visually reflects this concept.\n"
|
375 |
+
"Focus on realistic materials, lighting, and color palette. Use a 3D rendering style with natural surroundings."
|
376 |
+
)
|
377 |
+
|
378 |
+
|
379 |
+
try:
|
380 |
+
response = client.images.generate(
|
381 |
+
model="dall-e-3",
|
382 |
+
prompt=refined_prompt,
|
383 |
+
size="1024x1024",
|
384 |
+
quality="standard",
|
385 |
+
n=1
|
386 |
+
)
|
387 |
+
concept_image_url = response.data[0].url
|
388 |
+
st.image(concept_image_url, caption="Generated Concept Image", use_column_width=True)
|
389 |
+
st.markdown(f"[Download Image]({concept_image_url})", unsafe_allow_html=True)
|
390 |
+
|
391 |
+
st.session_state.generated_concept_image_url = concept_image_url
|
392 |
+
|
393 |
+
except Exception as e:
|
394 |
+
st.error(f"Image generation failed: {e}")
|
395 |
+
|
396 |
+
if next_step == "π Continue researching (codes, build types, regulations)":
|
397 |
+
st.markdown("#### π Research Hub")
|
398 |
+
st.info("Here are some useful topics and tools to help you dig deeper before you build.")
|
399 |
+
|
400 |
+
st.markdown("##### π§ Suggested Topics to Explore")
|
401 |
+
research_topics = {
|
402 |
+
"Prefab vs. Traditional Construction": "Compare pros and cons of prefabricated (modular) vs. traditional stick-built homes.",
|
403 |
+
"Land Use & Zoning": "Understand how land use bylaws and zoning affect what and where you can build.",
|
404 |
+
"Permit & Code Requirements": "Find out what permits are typically needed for a small home project in Canada.",
|
405 |
+
"Sustainable Building Materials": "Explore environmentally-friendly materials that are affordable and effective.",
|
406 |
+
"Off-Grid Living Regulations": "Learn the legal considerations if you want to live off-grid.",
|
407 |
+
"Energy Efficiency Standards": "Understand how to meet or exceed Canadian energy code requirements."
|
408 |
+
}
|
409 |
+
|
410 |
+
selected_topic = st.selectbox("Choose a topic to learn more:", ["Select a topic"] + list(research_topics.keys()))
|
411 |
+
if selected_topic != "Select a topic":
|
412 |
+
with st.spinner("Researching..."):
|
413 |
+
response = client.chat.completions.create(
|
414 |
+
model="gpt-4",
|
415 |
+
messages=[
|
416 |
+
{"role": "system", "content": "You are a Canadian construction research assistant."},
|
417 |
+
{"role": "user", "content": research_topics[selected_topic]}
|
418 |
+
],
|
419 |
+
temperature=0.7
|
420 |
+
)
|
421 |
+
st.markdown(f"### π {selected_topic}")
|
422 |
+
st.markdown(response.choices[0].message.content.strip())
|
423 |
+
|
424 |
+
st.markdown("##### π¬ Ask Your Own Research Question")
|
425 |
+
user_question = st.text_input("What else would you like to learn about?")
|
426 |
+
|
427 |
+
if st.button("Ask"):
|
428 |
+
if user_question:
|
429 |
+
with st.spinner("Getting the answer..."):
|
430 |
+
response = client.chat.completions.create(
|
431 |
+
model="gpt-4",
|
432 |
+
messages=[
|
433 |
+
{"role": "system", "content": "You're a helpful Canadian homebuilding advisor. Keep answers clear and based on current standards."},
|
434 |
+
{"role": "user", "content": user_question}
|
435 |
+
],
|
436 |
+
temperature=0.7
|
437 |
+
)
|
438 |
+
st.markdown("### π‘ Answer")
|
439 |
+
st.markdown(response.choices[0].message.content.strip())
|
440 |
+
else:
|
441 |
+
st.warning("Please type a question first.")
|
442 |
+
|
443 |
+
# === Helpful Resource Links (shown no matter what) ===
|
444 |
+
st.markdown("##### π Helpful Resources")
|
445 |
+
st.markdown("""
|
446 |
+
- [National Building Code of Canada (2020)](https://nrc.canada.ca/en/certifications-evaluations-standards/codes-canada/codes-canada-publications/national-building-code-canada-2020)
|
447 |
+
- [BC Building Code](https://www.bccodes.ca/)
|
448 |
+
- [Alberta Building Codes & Standards](https://www.alberta.ca/building-codes-and-standards.aspx)
|
449 |
+
- [Ontario Building Code](https://www.ontario.ca/page/building-code)
|
450 |
+
- [Quebec Construction Code](https://www.rbq.gouv.qc.ca/en/technical-buildings/buildings/construction-code.html)
|
451 |
+
- [Tiny Home Legal Info (Canada)](https://tinyhousecanada.ca/)
|
452 |
+
""")
|
453 |
+
# === 9. Eco-Friendly Upgrades Agent ===
|
454 |
+
|
455 |
+
st.markdown("---")
|
456 |
+
st.markdown("### π± Suggest Eco-Friendly Upgrades")
|
457 |
+
|
458 |
+
if st.button("Get Eco-Friendly Suggestions"):
|
459 |
+
if st.session_state.prompt.strip():
|
460 |
+
with st.spinner("Analyzing your layout for green upgrades..."):
|
461 |
+
|
462 |
+
def suggest_eco_upgrades(prompt):
|
463 |
+
system_instruction = (
|
464 |
+
"You are a sustainability expert in residential architecture. "
|
465 |
+
"Based on the following home design prompt, suggest 5 to 7 eco-friendly upgrades. "
|
466 |
+
"Be specific and include options like energy efficiency, water conservation, material choices, and renewable energy use. "
|
467 |
+
"Avoid general adviceβtailor suggestions to the design and layout described."
|
468 |
+
)
|
469 |
+
|
470 |
+
response = client.chat.completions.create(
|
471 |
+
model="gpt-4",
|
472 |
+
messages=[
|
473 |
+
{"role": "system", "content": system_instruction},
|
474 |
+
{"role": "user", "content": prompt}
|
475 |
+
],
|
476 |
+
temperature=0.7
|
477 |
+
)
|
478 |
+
|
479 |
+
return response.choices[0].message.content.strip()
|
480 |
+
|
481 |
+
eco_suggestions = suggest_eco_upgrades(st.session_state.prompt)
|
482 |
+
|
483 |
+
st.markdown("### β
Recommended Eco-Friendly Features")
|
484 |
+
st.markdown(eco_suggestions)
|
485 |
+
|
486 |
+
# Save to session for summary
|
487 |
+
st.session_state.eco_suggestions = eco_suggestions
|
488 |
+
|
489 |
+
else:
|
490 |
+
st.warning("Please enter a prompt first.")
|
491 |
|
492 |
|
493 |
|